<commit_before>const {JSWord, nullary} = require('../runtime/interfaces');

const fillRect = JSWord(function(x, y, width, height) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.fillRect(x, y, width, height);
});
fillRect.produces(0);

const translate = JSWord(function(x, y) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.translate(x, y);
});
translate.produces(0);

const lineWidth = JSWord(function(width) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.lineWidth = width;
});
lineWidth.produces(0);

const globalAlpha = JSWord(function(alpha) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.globalAlpha = alpha;
});
globalAlpha.produces(0);

const strokeStyle = JSWord(function(style) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.strokeStyle = style;
});
strokeStyle.produces(0);

const fillStyle = JSWord(function(style) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.fillStyle = style;
});
fillStyle.produces(0);

const beginPath = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.beginPath();
});
beginPath.produces(0);

const quadraticCurveTo = JSWord(function([x, y], [cx, cy]) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.quadraticCurveTo(x, y, cx, cy);
});
quadraticCurveTo.produces(0);

const moveTo = JSWord(function([x, y]) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.moveTo(x, y);
});
moveTo.produces(0);

const closePath = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.closePath();
});
closePath.produces(0);

const stroke = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.stroke();
});
stroke.produces(0);

const height = JSWord(function() {
  const {canvas} = arguments[arguments.length - 1];
  return canvas.height;
});
height.produces(1);

const width = JSWord(function() {
  const {canvas} = arguments[arguments.length - 1];
  return canvas.width;
});
width.produces(1);

module.exports = {
  '<height': height,
  '<width': width,
  fillRect,
  translate,
  lineWidth,
  globalAlpha,
  strokeStyle,
  fillStyle,
  beginPath,
  quadraticCurveTo,
  moveTo,
  closePath,
  stroke
};
<commit_msg>Add arc and fill words to canvas module<commit_after>const {JSWord, nullary} = require('../runtime/interfaces');

const fillRect = JSWord(function(x, y, width, height) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.fillRect(x, y, width, height);
});
fillRect.produces(0);

const translate = JSWord(function(x, y) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.translate(x, y);
});
translate.produces(0);

const lineWidth = JSWord(function(width) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.lineWidth = width;
});
lineWidth.produces(0);

const globalAlpha = JSWord(function(alpha) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.globalAlpha = alpha;
});
globalAlpha.produces(0);

const strokeStyle = JSWord(function(style) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.strokeStyle = style;
});
strokeStyle.produces(0);

const fillStyle = JSWord(function(style) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.fillStyle = style;
});
fillStyle.produces(0);

const beginPath = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.beginPath();
});
beginPath.produces(0);

const quadraticCurveTo = JSWord(function([x, y], [cx, cy]) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.quadraticCurveTo(x, y, cx, cy);
});
quadraticCurveTo.produces(0);

const moveTo = JSWord(function([x, y]) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.moveTo(x, y);
});
moveTo.produces(0);

const arc = JSWord(function([x, y], r, startAngle, endAngle, anticlockwise) {
  const {ctx} = arguments[arguments.length - 1];
  ctx.arc(x, y, r, startAngle, endAngle, anticlockwise);
});
arc.produces(0);

const closePath = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.closePath();
});
closePath.produces(0);

const stroke = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.stroke();
});
stroke.produces(0);

const fill = JSWord(function() {
  const {ctx} = arguments[arguments.length - 1];
  ctx.fill();
});
fill.produces(0);

const height = JSWord(function() {
  const {canvas} = arguments[arguments.length - 1];
  return canvas.height;
});
height.produces(1);

const width = JSWord(function() {
  const {canvas} = arguments[arguments.length - 1];
  return canvas.width;
});
width.produces(1);

module.exports = {
  '<height': height,
  '<width': width,
  fillRect,
  translate,
  lineWidth,
  globalAlpha,
  strokeStyle,
  fillStyle,
  beginPath,
  quadraticCurveTo,
  moveTo,
  closePath,
  stroke,
  arc,
  fill
};
<|endoftext|>


<commit_before>import React from 'react'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import config from '../utils/siteConfig'
import Layout from '../components/Layout'
import Hero from '../components/Hero'
import BlogList from '../components/Blog/BlogList'

import { Flex, Box } from 'rebass'

import SEO from '../components/SEO'

const Blog2 = ({ data, location }) => {
  const posts = data.allContentfulPost.edges
  const blog = data.contentfulBlog
  return (
    <Layout location={location}>
      <Helmet>
        <title>{`${config.siteTitle} - Blog`}</title>
      </Helmet>
      <SEO postNode={blog} pagePath="contact" customTitle pageSEO />
      <>
        <Flex flexWrap="wrap" mb={[5, 0]} className="changeDirection">
          <Box p={[3, 4]} width={[1, 1, 1 / 2, 1 / 3]}>
            <Box p={[3, 4]} width={[1]}>
              <article
                dangerouslySetInnerHTML={{
                  __html: blog.body.childMarkdownRemark.html,
                }}
              />
            </Box>
            <Flex px={[3, 4]} width={[1]} flexWrap="wrap" flexDirection="row">
              {posts.map(({ node: post }) => (
                <BlogList
                  key={post.id}
                  slug={post.slug}
                  image={post.heroImage}
                  title={post.title}
                  date={post.publishDate}
                  time={post.body.childMarkdownRemark.timeToRead}
                  excerpt={post.body}
                />
              ))}
            </Flex>
          </Box>
          <Box p={0} width={[1, 1, 1 / 2, 2 / 3]}>
            <Hero image={blog.heroImage} />
          </Box>
        </Flex>
      </>
    </Layout>
  )
}

export const query = graphql`
  query {
    allContentfulPost(
      limit: 1000
      sort: { fields: [publishDate], order: DESC }
    ) {
      edges {
        node {
          title
          id
          slug
          publishDate(formatString: "DD MMM YYYY")
          heroImage {
            title
            fluid(maxWidth: 1000) {
              ...GatsbyContentfulFluid_withWebp
            }
          }
          body {
            childMarkdownRemark {
              html
              excerpt(pruneLength: 240)
              timeToRead
            }
          }
        }
      }
    }
    contentfulBlog {
      title
      id
      heroImage {
        title
        fluid(maxWidth: 1000) {
          ...GatsbyContentfulFluid_withWebp
        }
        ogimg: resize(width: 900) {
          src
          width
          height
        }
      }
      body {
        childMarkdownRemark {
          html
        }
      }
    }
  }
`

export default Blog2
<commit_msg>Update blog.js<commit_after>import React from 'react'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import config from '../utils/siteConfig'
import Layout from '../components/Layout'
import Hero from '../components/Hero'
import BlogList from '../components/Blog/BlogList'

import { Flex, Box } from 'rebass'

import SEO from '../components/SEO'

const Blog2 = ({ data, location }) => {
  const posts = data.allContentfulPost.edges
  const blog = data.contentfulBlog
  return (
    <Layout location={location}>
      <Helmet>
        <title>{`${config.siteTitle} - Blog`}</title>
      </Helmet>
      <SEO postNode={blog} pagePath="contact" customTitle pageSEO />
      <>
        <Flex flexWrap="wrap" mb={[5, 0]} className="changeDirection">
          <Box p={[3, 4]} width={[1, 1, 1 / 2, 1 / 3]}>
            <Box p={[3, 4]} width={[1]}>
              <article
                dangerouslySetInnerHTML={{
                  __html: blog.body.childMarkdownRemark.html,
                }}
              />
            </Box>
            <Flex width={[1]} flexWrap="wrap" flexDirection="row">
              {posts.map(({ node: post }) => (
                <BlogList
                  key={post.id}
                  slug={post.slug}
                  image={post.heroImage}
                  title={post.title}
                  date={post.publishDate}
                  time={post.body.childMarkdownRemark.timeToRead}
                  excerpt={post.body}
                />
              ))}
            </Flex>
          </Box>
          <Box p={0} width={[1, 1, 1 / 2, 2 / 3]}>
            <Hero image={blog.heroImage} />
          </Box>
        </Flex>
      </>
    </Layout>
  )
}

export const query = graphql`
  query {
    allContentfulPost(
      limit: 1000
      sort: { fields: [publishDate], order: DESC }
    ) {
      edges {
        node {
          title
          id
          slug
          publishDate(formatString: "DD MMM YYYY")
          heroImage {
            title
            fluid(maxWidth: 1000) {
              ...GatsbyContentfulFluid_withWebp
            }
          }
          body {
            childMarkdownRemark {
              html
              excerpt(pruneLength: 240)
              timeToRead
            }
          }
        }
      }
    }
    contentfulBlog {
      title
      id
      heroImage {
        title
        fluid(maxWidth: 1000) {
          ...GatsbyContentfulFluid_withWebp
        }
        ogimg: resize(width: 900) {
          src
          width
          height
        }
      }
      body {
        childMarkdownRemark {
          html
        }
      }
    }
  }
`

export default Blog2
<|endoftext|>


<commit_before>"use strict";

class Pagination{

	constructor(el, options = {}){
		this._length        = 0;
		this.startRange     = 3;
		this.endRange       = 3;
		this.activeRange    = 1;
		this.links          = [];

		this.el             = el;
		this.linkTemplate   = options.linkTemplate;
		this.clipTemplate   = options.clipTemplate || "…";
		this.length         = options.length || 20;
		this.active         = options.active || 10;
	}

	/**
	 * Create an element from a template variable. Internal use only.
	 *
	 * @private
	 * @param {Function|Node|String} template
	 * @param {Array} args - Arguments passed to template variable if it's a function
	 * @return {Node}
	 */
	_create(template, args){

		/** Short-circuit falsy values */
		if(!template) return;

		let result = template;

		/** Invoke a callback to generate the link element */
		if("function" === typeof template)
			result = template.apply(null, args);

		/** The callback returned a string, so use it to construct an HTML element */
		if("[object String]" === toString.call(result))
			result = this._parseHTML(result);

		if(result instanceof Node)
			result = result.cloneNode(true);

		return result;
	}

	/**
	 * Create a link element for an index.
	 *
	 * @param {Number} index - Zero-based page index
	 * @return {HTMLElement}
	 */
	createLink(index){
		let result = (
			this._create(this._linkTemplate, [index, this]) ||
			New("a", { href: "#" })
		).cloneNode(true);

		/** Set the link's text, unless an author used a callback (we'll assume they took care of that) */
		if("function" !== typeof this._linkTemplate){
			let label = deepest(result);
			if(!label.childNodes.length)
				label.appendChild(document.createTextNode(""));
			label.childNodes[0].data = index + 1;
		}

		result.addEventListener(pressEvent, e => {
			this.active = index;
			e.preventDefault();
			return false;
		});

		result.setAttribute("data-index", index);
		return result;
	}

	/**
	 * Return a blueprint for generating new elements. Internal use only.
	 *
	 * Values may be an HTML string, an element reference, or a function that
	 * returns an element. Functions are called with two parameters: the page
	 * link's index, and a reference to the owning Pagination object.
	 *
	 * The returned array holds two values: the template value, and an integer
	 * representing how it's supposed to be used.
	 *
	 * @private
	 * @param {Function|HTMLElement|String} input
	 * @return {Array}
	 */
	_parseTemplate(input){

		/** Short-circuit for falsy values */
		if(!input) return;

		/** DOM element */
		else if(input instanceof Element){

			/** Detach element from the DOM if it's attached */
			const parent = input.parentNode;
			parent && parent.removeChild(input);

			return input;
		}

		/** If it's a string, use it to construct a new HTML element */
		else if("[object String]" === toString.call(input))
			return this._parseHTML(input);

		/** Alternatively, accept a function that returns an element */
		else if("function" === typeof input)
			return input;
	}

	/**
	 * Parse an HTML block and return the first element contained in the result.
	 *
	 * If the fragment holds no elements, the first text node is returned instead.
	 *
	 * @private
	 * @param {String} input
	 * @return {Node}
	 */
	_parseHTML(input){
		let frag = document.createDocumentFragment();
		let root = frag.appendChild(New("div"));
		root.insertAdjacentHTML("afterbegin", input);
		return root.removeChild(root.firstElementChild || root.firstChild);
	}

	/**
	 * Refresh the display of the pagination links with the current settings.
	 */
	rebuild(){
		if(!this.links[this._active]) return;

		/** Drop each link */
		const el = this.el;
		while(el.firstChild)
			el.removeChild(el.firstChild);

		const left  = this._active - this.activeRange;
		const right = this._active + this.activeRange;
		const end   = this._length - this.endRange;
		const children = [];

		/** Reattach leading range */
		for(let i = 0; i < this.startRange; ++i)
			children.push(el.appendChild(this.links[i]));

		/** Should we display a truncation indicator? */
		if(left > this.startRange)
			el.appendChild(this.leftClip);

		/** Display the active range */
		for(let i = Math.max(this.startRange, left); i <= Math.min(this._length - 1, right); ++i)
			children.push(el.appendChild(this.links[i]));

		/** Check if we should display a truncation indicator for the right-side too */
		if(right < end)
			el.appendChild(this.rightClip);

		/** Reattach trailing range */
		for(let i = end; i < this._length; ++i)
			children.push(el.appendChild(this.links[i]));

		/** Run through each node that was added and check their classes list */
		for(let i = 0, l = children.length; i < l; ++i){
			let elIndex = +children[i].getAttribute("data-index");
			children[i].classList[elIndex === this._active ? "add" : "remove"]("active");
		}
	}

	/**
	 * The blueprint used to generate new page links.
	 *
	 * @type {HTMLElement}
	 */
	get linkTemplate(){ return this._linkTemplate }
	set linkTemplate(input){

		/** No change? Don't bother */
		if(input === this._linkTemplate) return;

		this._linkTemplate = this._parseTemplate(input);
	}

	/**
	 * The blueprint for generating truncation indicators.
	 *
	 * @type {HTMLElement}
	 */
	get clipTemplate(){ return this._clipTemplate }
	set clipTemplate(input){

		/** Sanity check */
		if(input === this._clipTemplate) return;

		this._clipTemplate = this._parseTemplate(input);
		this.leftClip      = this._create(this._clipTemplate, "left");
		this.rightClip     = this._create(this._clipTemplate, "right");
	}

	get length(){ return this._length || 0 }
	set length(input){
		input = +input;
		if(input < 1) input = 1;
		if(input !== this._length){

			/** We're extending the current page range */
			if(input > this._length)
				for(let i = this._length; i < input; ++i){
					let link = this.links[i];
					link || this.links.push(link = this.createLink(i));
					this.el.appendChild(link);
				}

			/** Shrinking the page range */
			else for(let i = this._length - 1; i >= input; --i){
				let link = this.links[i];
				if(link.parentNode)
					link.parentNode.removeChild(link);
			}

			this._length = input;

			/** The currently active index exceeds the new bounds. Cap it. */
			if(input <= this._active)
				this.active = input - 1;

			/** Otherwise, check clipping range */
			else this.rebuild();
		}
	}

	get active(){ return this._active || 0 }
	set active(input){
		input = +input;

		/** Ensure input falls within valid range */
		let l = this.length;
		if(input < 0)       input = 0;
		else if(input >= l) input = l - 1;

		if(input !== this._active){
			this._active = input;
			this.rebuild();
		}
	}
}
<commit_msg>Add option to customise name of active-state's CSS class<commit_after>"use strict";

class Pagination{

	constructor(el, options = {}){
		this._length        = 0;
		this.startRange     = 3;
		this.endRange       = 3;
		this.activeRange    = 1;
		this.links          = [];

		this.el             = el;
		this.linkTemplate   = options.linkTemplate;
		this.clipTemplate   = options.clipTemplate || "…";
		this.activeClass    = options.activeClass  || "active";
		this.length         = options.length || 20;
		this.active         = options.active || 10;
	}

	/**
	 * Create an element from a template variable. Internal use only.
	 *
	 * @private
	 * @param {Function|Node|String} template
	 * @param {Array} args - Arguments passed to template variable if it's a function
	 * @return {Node}
	 */
	_create(template, args){

		/** Short-circuit falsy values */
		if(!template) return;

		let result = template;

		/** Invoke a callback to generate the link element */
		if("function" === typeof template)
			result = template.apply(null, args);

		/** The callback returned a string, so use it to construct an HTML element */
		if("[object String]" === toString.call(result))
			result = this._parseHTML(result);

		if(result instanceof Node)
			result = result.cloneNode(true);

		return result;
	}

	/**
	 * Create a link element for an index.
	 *
	 * @param {Number} index - Zero-based page index
	 * @return {HTMLElement}
	 */
	createLink(index){
		let result = (
			this._create(this._linkTemplate, [index, this]) ||
			New("a", { href: "#" })
		).cloneNode(true);

		/** Set the link's text, unless an author used a callback (we'll assume they took care of that) */
		if("function" !== typeof this._linkTemplate){
			let label = deepest(result);
			if(!label.childNodes.length)
				label.appendChild(document.createTextNode(""));
			label.childNodes[0].data = index + 1;
		}

		result.addEventListener(pressEvent, e => {
			this.active = index;
			e.preventDefault();
			return false;
		});

		result.setAttribute("data-index", index);
		return result;
	}

	/**
	 * Return a blueprint for generating new elements. Internal use only.
	 *
	 * Values may be an HTML string, an element reference, or a function that
	 * returns an element. Functions are called with two parameters: the page
	 * link's index, and a reference to the owning Pagination object.
	 *
	 * The returned array holds two values: the template value, and an integer
	 * representing how it's supposed to be used.
	 *
	 * @private
	 * @param {Function|HTMLElement|String} input
	 * @return {Array}
	 */
	_parseTemplate(input){

		/** Short-circuit for falsy values */
		if(!input) return;

		/** DOM element */
		else if(input instanceof Element){

			/** Detach element from the DOM if it's attached */
			const parent = input.parentNode;
			parent && parent.removeChild(input);

			return input;
		}

		/** If it's a string, use it to construct a new HTML element */
		else if("[object String]" === toString.call(input))
			return this._parseHTML(input);

		/** Alternatively, accept a function that returns an element */
		else if("function" === typeof input)
			return input;
	}

	/**
	 * Parse an HTML block and return the first element contained in the result.
	 *
	 * If the fragment holds no elements, the first text node is returned instead.
	 *
	 * @private
	 * @param {String} input
	 * @return {Node}
	 */
	_parseHTML(input){
		let frag = document.createDocumentFragment();
		let root = frag.appendChild(New("div"));
		root.insertAdjacentHTML("afterbegin", input);
		return root.removeChild(root.firstElementChild || root.firstChild);
	}

	/**
	 * Refresh the display of the pagination links with the current settings.
	 */
	rebuild(){
		if(!this.links[this._active]) return;

		/** Drop each link */
		const el = this.el;
		while(el.firstChild)
			el.removeChild(el.firstChild);

		const left  = this._active - this.activeRange;
		const right = this._active + this.activeRange;
		const end   = this._length - this.endRange;
		const children = [];

		/** Reattach leading range */
		for(let i = 0; i < this.startRange; ++i)
			children.push(el.appendChild(this.links[i]));

		/** Should we display a truncation indicator? */
		if(left > this.startRange)
			el.appendChild(this.leftClip);

		/** Display the active range */
		for(let i = Math.max(this.startRange, left); i <= Math.min(this._length - 1, right); ++i)
			children.push(el.appendChild(this.links[i]));

		/** Check if we should display a truncation indicator for the right-side too */
		if(right < end)
			el.appendChild(this.rightClip);

		/** Reattach trailing range */
		for(let i = end; i < this._length; ++i)
			children.push(el.appendChild(this.links[i]));

		/** Run through each node that was added and check their classes list */
		for(let i = 0, l = children.length; i < l; ++i){
			let elIndex = +children[i].getAttribute("data-index");
			children[i].classList[elIndex === this._active ? "add" : "remove"](this.activeClass);
		}
	}

	/**
	 * The blueprint used to generate new page links.
	 *
	 * @type {HTMLElement}
	 */
	get linkTemplate(){ return this._linkTemplate }
	set linkTemplate(input){

		/** No change? Don't bother */
		if(input === this._linkTemplate) return;

		this._linkTemplate = this._parseTemplate(input);
	}

	/**
	 * The blueprint for generating truncation indicators.
	 *
	 * @type {HTMLElement}
	 */
	get clipTemplate(){ return this._clipTemplate }
	set clipTemplate(input){

		/** Sanity check */
		if(input === this._clipTemplate) return;

		this._clipTemplate = this._parseTemplate(input);
		this.leftClip      = this._create(this._clipTemplate, "left");
		this.rightClip     = this._create(this._clipTemplate, "right");
	}

	get length(){ return this._length || 0 }
	set length(input){
		input = +input;
		if(input < 1) input = 1;
		if(input !== this._length){

			/** We're extending the current page range */
			if(input > this._length)
				for(let i = this._length; i < input; ++i){
					let link = this.links[i];
					link || this.links.push(link = this.createLink(i));
					this.el.appendChild(link);
				}

			/** Shrinking the page range */
			else for(let i = this._length - 1; i >= input; --i){
				let link = this.links[i];
				if(link.parentNode)
					link.parentNode.removeChild(link);
			}

			this._length = input;

			/** The currently active index exceeds the new bounds. Cap it. */
			if(input <= this._active)
				this.active = input - 1;

			/** Otherwise, check clipping range */
			else this.rebuild();
		}
	}

	get active(){ return this._active || 0 }
	set active(input){
		input = +input;

		/** Ensure input falls within valid range */
		let l = this.length;
		if(input < 0)       input = 0;
		else if(input >= l) input = l - 1;

		if(input !== this._active){
			this._active = input;
			this.rebuild();
		}
	}
}
<|endoftext|>


<commit_before>var dl = require('datalib'),
    parseProperties = require('./properties');

function parseMark(model, mark/*, [isMark]*/) {
  var props = mark.properties,
      group = mark.marks,
      config = model._config,
      arglen = arguments.length,
      argidx = 2,
      isMark = false;

  if (arglen > argidx && dl.isBoolean(arguments[arglen - 1])) {
    isMark = true;
  }

  // for scatter plots, set symbol size specified in config if not in spec
  if (typeof props !== 'undefined') {
    var enter = props['enter'];
    if (mark.type === 'symbol') {
      if (enter && !enter['size'] && config && config.marks && config.marks.symbolSize) {
        enter['size'] = {value: model._config.marks.symbolSize};
      }
    }
  }

  // Object defines whether to set the stroke or
  // fill to the given default color
  var colorMap = {
    symbol: 'fill',
    arc: 'fill',
    area: 'fill',
    rect: 'fill',
    path: 'stroke',
    line: 'stroke',
    rule: 'stroke',
    text: 'stroke'
  }

  // Set default mark color if no color is given in spec
  if (isMark) {
    var property = colorMap[mark.type];
    if (typeof props !== 'undefined' && 'enter' in props && !(property in props['enter'])) {
      setDefaultColor(property, props, config);
    }
  }

  // parse mark property definitions
  dl.keys(props).forEach(function(k) {
    props[k] = parseProperties(model, mark.type, props[k]);
  });

  // parse delay function
  if (mark.delay) {
    mark.delay = parseProperties(model, mark.type, {delay: mark.delay});
  }

  // recurse if group type
  if (group) {
    mark.marks = group.map(function(g) { return parseMark(model, g); });
  }

  return mark;
}

function setDefaultColor(property, props, config) {
  var color = '#000000';
  if (typeof config !== 'undefined' && 'marks' in config && 'color' in config.marks) {
    color =  config.marks['color'];
  }
  props['enter'][property] = {
    'value': color
  };
}

module.exports = parseMark;

parseMark.schema = {
  "defs": {
    "mark": {
      "type": "object",

      "properties": {
        "name": {"type": "string"},
        "key": {"type": "string"},
        "type": {"enum": ["rect", "symbol", "path", "arc",
          "area", "line", "rule", "image", "text", "group"]},

        "from": {
          "type": "object",
          "properties": {
            "data": {"type": "string"},
            "mark": {"type": "string"},
            "transform": {"$ref": "#/defs/transform"}
          },
          "additionalProperties": false
        },

        "delay": {"$ref": "#/refs/numberValue"},
        "ease": {
          "enum": ["linear", "quad", "cubic", "sin",
            "exp", "circle", "bounce"].reduce(function(acc, e) {
              ["in", "out", "in-out", "out-in"].forEach(function(m) {
                acc.push(e+"-"+m);
              });
              return acc;
          }, [])
        },

        "interactive": {"type": "boolean"},

        "properties": {
          "type": "object",
          "properties": {
            "enter":  {"$ref": "#/defs/propset"},
            "update": {"$ref": "#/defs/propset"},
            "exit":   {"$ref": "#/defs/propset"},
            "hover":  {"$ref": "#/defs/propset"}
          },
          "additionalProperties": false,
          "anyOf": [{"required": ["enter"]}, {"required": ["update"]}]
        }
      },

      // "additionalProperties": false,
      "required": ["type"]
    }
  }
};
<commit_msg>Cleanup parse mark.<commit_after>var dl = require('datalib'),
    parseProperties = require('./properties');

function parseMark(model, mark, applyDefaults) {
  var props = mark.properties || (mark.properties = {}),
      enter = props.enter || (props.enter = {}),
      group = mark.marks,
      config = model.config().marks || {};

  if (applyDefaults) {
    // for scatter plots, set symbol size specified in config if not in spec
    if (mark.type === 'symbol' && !enter.size && config.symbolSize) {
        enter.size = {value: config.symbolSize};
    }

    // Themes define a default "color" that maps to fill/stroke based on mark type.
    var colorMap = {
      arc: 'fill', area: 'fill', rect: 'fill', symbol: 'fill', text: 'fill',
      line: 'stroke', path: 'stroke', rule: 'stroke'
    };

    // Set default mark color if no color is given in spec, and only do so for
    // user-defined marks (not axis/legend marks).
    var colorProp = colorMap[mark.type];
    if (!enter[colorProp] && config.color) {
      enter[colorProp] = {value: config.color};
    }
  }

  // parse mark property definitions
  dl.keys(props).forEach(function(k) {
    props[k] = parseProperties(model, mark.type, props[k]);
  });

  // parse delay function
  if (mark.delay) {
    mark.delay = parseProperties(model, mark.type, {delay: mark.delay});
  }

  // recurse if group type
  if (group) {
    mark.marks = group.map(function(g) { return parseMark(model, g, true); });
  }

  return mark;
}

module.exports = parseMark;

parseMark.schema = {
  "defs": {
    "mark": {
      "type": "object",

      "properties": {
        "name": {"type": "string"},
        "key": {"type": "string"},
        "type": {"enum": ["rect", "symbol", "path", "arc",
          "area", "line", "rule", "image", "text", "group"]},

        "from": {
          "type": "object",
          "properties": {
            "data": {"type": "string"},
            "mark": {"type": "string"},
            "transform": {"$ref": "#/defs/transform"}
          },
          "additionalProperties": false
        },

        "delay": {"$ref": "#/refs/numberValue"},
        "ease": {
          "enum": ["linear", "quad", "cubic", "sin",
            "exp", "circle", "bounce"].reduce(function(acc, e) {
              ["in", "out", "in-out", "out-in"].forEach(function(m) {
                acc.push(e+"-"+m);
              });
              return acc;
          }, [])
        },

        "interactive": {"type": "boolean"},

        "properties": {
          "type": "object",
          "properties": {
            "enter":  {"$ref": "#/defs/propset"},
            "update": {"$ref": "#/defs/propset"},
            "exit":   {"$ref": "#/defs/propset"},
            "hover":  {"$ref": "#/defs/propset"}
          },
          "additionalProperties": false,
          "anyOf": [{"required": ["enter"]}, {"required": ["update"]}]
        }
      },

      // "additionalProperties": false,
      "required": ["type"]
    }
  }
};
<|endoftext|>


<commit_before>import $ from "jquery";
import _ from "underscore";
import _t from "@patternslib/patternslib/src/core/i18n";
import Base from "@patternslib/patternslib/src/core/base";
import logging from "@patternslib/patternslib/src/core/logging";
import Parser from "@patternslib/patternslib/src/core/parser";

const log = logging.getLogger("pat-upload");

export const parser = new Parser("upload");
parser.add_argument("concurrent-uploads", "multiple", ["multiple", "single"]); // Only one upload at a time, or multiple ones simultaneously?
parser.add_argument("ajax-upload", true); //boolean: true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
parser.add_argument("post-upload", true); //boolean: condition value for the file preview in div element to fadeout after file upload is completed. (true)
parser.add_argument("class-name", "upload"); //string: value for class attribute in the form element ('upload')
parser.add_argument("clickable", "true"); //boolean: If you can click on container to also upload (true)
parser.add_argument("current-path"); //string: Current path related items is starting with (null)
parser.add_argument("label", "Drop files here…"); //string: Text to show instead of the default ('Drop files here…')
parser.add_argument("name", "file"); //string: value for name attribute in the file input element ('file')
parser.add_argument("previews-container", ".previews"); //selector: JavaScript selector for file preview in div element. (.previews)
parser.add_argument("timeout", 120000);
parser.add_argument("trigger", "button"); //string: What triggers the upload.  'button' expects user to click upload button, 'auto' starts uploading automatically after the user drags something, and always hides the upload button. ('button')
parser.add_argument("url"); //string: If not used with a form, this option must provide the URL to submit to (null)
parser.add_argument("headers"); // Optional headers as String in the form of "{'Key': 'Value'}"

export default Base.extend({
    name: "upload",
    trigger: ".pat-upload",
    parser: "patternslib",
    defaults: {
        addRemoveLinks: false,
        maxFiles: null,
        maxFilesize: 99999999, // let's not have a max by default...
        previewTemplate: null,
    },
    template_preview: null,

    async init($el, opts) {
        if (window.__patternslib_import_styles) {
            import("dropzone/dist/basic.css");
        }

        const Dropzone = (await import("dropzone")).default;
        Dropzone.autoDiscover = false; // we do not want this plugin to auto discover
        this.template_preview = (await import("./templates/preview.html")).default;
        const template_upload = (await import("./templates/upload.html")).default;

        this.cfgs = _.extend(_.clone(this.defaults), parser.parse($el, opts, true)[0]);
        this.$el.addClass(this.cfgs.className);
        this.$el.append(
            _.template(template_upload)({ _t: _t, label: _t(this.cfgs.label) })
        );

        if (!this.cfgs.ajaxUpload) {
            // no ajax upload, drop the fallback
            $(".fallback", this.$el).remove();
        }
        try {
            // if init of Dropzone fails it says nothing and
            // it fails silently. Using this block we make sure
            // that if you break it w/ some weird or missing option
            // you can get a proper log of it
            this.dropzone = new Dropzone(
                $(".upload-area", this.$el)[0],
                this.getDzoneOptions()
            );
        } catch (e) {
            log.error(e);
            throw e;
        }
        this.registerHandlers();
    },

    refresh() {
        const subform = this.el.closest(".pat-subform");
        if (subform) {
            const button = subform.querySelector("button[type='submit']");
            button?.click();
        } else {
            const form = this.el.closest("form");
            if (form.matches(".pat-inject")) {
                form.submit();
            }
        }
    },

    registerHandlers() {
        this.dropzone.on(
            "complete",
            $.proxy(function (file) {
                if (this.cfgs.postUpload) {
                    setTimeout(function () {
                        $(file.previewElement).fadeOut();
                    }, 1000);
                }
                if (
                    _.filter(this.dropzone.files, function (f) {
                        return f.status !== "success";
                    }).length === 0
                ) {
                    this.refresh();
                }
            }, this)
        );
    },

    getUrl() {
        let url = this.cfgs.url;
        if (!url) {
            const $form = this.$el.closest("form");
            url = $form.length > 0 ? $form.attr("action") : window.location.href;
        }
        return url;
    },

    getDzoneOptions() {
        if (typeof this.cfgs.clickable === "string") {
            this.cfgs.clickable = this.cfgs.clickable === "true" ? true : false;
        }
        const options = $.extend({}, this.cfgs);
        delete options.postUpload;
        if (this.cfgs.previewsContainer) {
            /* If they have a select but it's not an id, let's make an id selector
             * so we can target the correct container. dropzone is weird here...
             */
            const $preview = this.$el.find(this.cfgs.previewsContainer);
            if ($preview.length > 0) {
                options.previewsContainer = $preview[0];
            }
        }
        options.url = this.getUrl();
        options.uploadMultiple = options.concurrentUploads === "multiple" ? true : false;
        options.previewTemplate = this.template_preview;
        // if our element is a form we should force some values
        // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone

        try {
            if (this.cfgs.headers) {
                options.headers = JSON.parse(this.cfgs.headers);
            }
        } catch (e) {
            log.warn("Could not parse the headers option");
        }

        return options;
    },
});
<commit_msg>maint: Add mode debugging statements.<commit_after>import $ from "jquery";
import _ from "underscore";
import _t from "@patternslib/patternslib/src/core/i18n";
import Base from "@patternslib/patternslib/src/core/base";
import logging from "@patternslib/patternslib/src/core/logging";
import Parser from "@patternslib/patternslib/src/core/parser";

const log = logging.getLogger("pat-upload");

export const parser = new Parser("upload");
parser.add_argument("concurrent-uploads", "multiple", ["multiple", "single"]); // Only one upload at a time, or multiple ones simultaneously?
parser.add_argument("ajax-upload", true); //boolean: true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
parser.add_argument("post-upload", true); //boolean: condition value for the file preview in div element to fadeout after file upload is completed. (true)
parser.add_argument("class-name", "upload"); //string: value for class attribute in the form element ('upload')
parser.add_argument("clickable", "true"); //boolean: If you can click on container to also upload (true)
parser.add_argument("current-path"); //string: Current path related items is starting with (null)
parser.add_argument("label", "Drop files here…"); //string: Text to show instead of the default ('Drop files here…')
parser.add_argument("name", "file"); //string: value for name attribute in the file input element ('file')
parser.add_argument("previews-container", ".previews"); //selector: JavaScript selector for file preview in div element. (.previews)
parser.add_argument("timeout", 120000);
parser.add_argument("trigger", "button"); //string: What triggers the upload.  'button' expects user to click upload button, 'auto' starts uploading automatically after the user drags something, and always hides the upload button. ('button')
parser.add_argument("url"); //string: If not used with a form, this option must provide the URL to submit to (null)
parser.add_argument("headers"); // Optional headers as String in the form of "{'Key': 'Value'}"

export default Base.extend({
    name: "upload",
    trigger: ".pat-upload",
    parser: "patternslib",
    defaults: {
        addRemoveLinks: false,
        maxFiles: null,
        maxFilesize: 99999999, // let's not have a max by default...
        previewTemplate: null,
    },
    template_preview: null,

    async init($el, opts) {
        if (window.__patternslib_import_styles) {
            import("dropzone/dist/basic.css");
        }

        const Dropzone = (await import("dropzone")).default;
        Dropzone.autoDiscover = false; // we do not want this plugin to auto discover
        this.template_preview = (await import("./templates/preview.html")).default;
        const template_upload = (await import("./templates/upload.html")).default;

        this.cfgs = _.extend(_.clone(this.defaults), parser.parse($el, opts, true)[0]);
        this.$el.addClass(this.cfgs.className);
        this.$el.append(
            _.template(template_upload)({ _t: _t, label: _t(this.cfgs.label) })
        );

        if (!this.cfgs.ajaxUpload) {
            // no ajax upload, drop the fallback
            $(".fallback", this.$el).remove();
        }
        try {
            // if init of Dropzone fails it says nothing and
            // it fails silently. Using this block we make sure
            // that if you break it w/ some weird or missing option
            // you can get a proper log of it
            this.dropzone = new Dropzone(
                $(".upload-area", this.$el)[0],
                this.getDzoneOptions()
            );
        } catch (e) {
            log.error(e);
            throw e;
        }
        this.registerHandlers();
    },

    refresh() {
        const subform = this.el.closest(".pat-subform");
        if (subform) {
            log.debug("Refresh subform");
            const button = subform.querySelector("button[type='submit']");
            button?.click();
        } else {
            const form = this.el.closest("form");
            if (form && form.matches(".pat-inject")) {
                log.debug("Refresh form");
                form.submit();
            }
        }
    },

    registerHandlers() {
        this.dropzone.on(
            "complete",
            $.proxy(function (file) {
                if (this.cfgs.postUpload) {
                    setTimeout(function () {
                        $(file.previewElement).fadeOut();
                    }, 1000);
                }
                if (
                    _.filter(this.dropzone.files, function (f) {
                        return f.status !== "success";
                    }).length === 0
                ) {
                    this.refresh();
                }
            }, this)
        );
    },

    getUrl() {
        let url = this.cfgs.url;
        if (!url) {
            const $form = this.$el.closest("form");
            url = $form.length > 0 ? $form.attr("action") : window.location.href;
        }
        return url;
    },

    getDzoneOptions() {
        if (typeof this.cfgs.clickable === "string") {
            this.cfgs.clickable = this.cfgs.clickable === "true" ? true : false;
        }
        const options = $.extend({}, this.cfgs);
        delete options.postUpload;
        if (this.cfgs.previewsContainer) {
            /* If they have a select but it's not an id, let's make an id selector
             * so we can target the correct container. dropzone is weird here...
             */
            const $preview = this.$el.find(this.cfgs.previewsContainer);
            if ($preview.length > 0) {
                options.previewsContainer = $preview[0];
            }
        }
        options.url = this.getUrl();
        options.uploadMultiple = options.concurrentUploads === "multiple" ? true : false;
        options.previewTemplate = this.template_preview;
        // if our element is a form we should force some values
        // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone

        try {
            if (this.cfgs.headers) {
                options.headers = JSON.parse(this.cfgs.headers);
            }
        } catch (e) {
            log.warn("Could not parse the headers option");
        }

        return options;
    },
});
<|endoftext|>


<commit_before>import * as bt from '@babel/types';
import fs from 'fs';
import gettextParser from 'gettext-parser';
import { DEFAULT_HEADERS, PO_PRIMITIVES, LOCATION } from './defaults';
import dedent from 'dedent';

export function buildPotData(translations) {
    const data = {
        charset: 'UTF-8',
        headers: DEFAULT_HEADERS,
        translations: {
            '': {
            },
        },
    };

    for (const trans of translations) {
        const ctx = trans[PO_PRIMITIVES.MSGCTXT] || '';
        if (!data.translations[ctx]) {
            data.translations[ctx] = {};
        }

        if (!data.translations[ctx][trans.msgid]) {
            data.translations[ctx][trans.msgid] = trans;
            continue;
        }

        const oldTrans = data.translations[ctx][trans.msgid];

        // merge references
        if (oldTrans.comments && oldTrans.comments.reference &&
            trans.comments && trans.comments.reference &&
            ! oldTrans.comments.reference.match(trans.comments.reference)) {
            oldTrans.comments.reference = `${oldTrans.comments.reference}\n${trans.comments.reference}`;
        }
    }

    return data;
}

export function applyReference(poEntry, node, filepath, location) {
    if (!poEntry.comments) {
        poEntry.comments = {};
    }

    let reference = null;

    switch (location) {
        case LOCATION.FILE:
            reference = filepath; break;
        case LOCATION.NEVER:
            reference = null; break;
        default:
            reference = `${filepath}:${node.loc.start.line}`;
    }

    poEntry.comments.reference = reference;
    return poEntry;
}

const tagRegex = {};
export function applyExtractedComments(poEntry, nodePath, tag) {
    if (!poEntry.comments) {
        poEntry.comments = {};
    }

    const node = nodePath.node;

    if (!(
        bt.isStatement(node) ||
        bt.isDeclaration(node)
    )) {
        // Collect parents' comments
        //
        applyExtractedComments(poEntry, nodePath.parentPath, tag);
    }

    const comments = node.leadingComments;
    let transComments = comments ? comments.map((c) => c.value) : [];
    if (tag) {
        if (!tagRegex[tag]) {
            tagRegex[tag] = new RegExp(`^\\s*${tag}\\s*(.*?)\\s*$`);
        }
        transComments = transComments
            .map((c) => c.match(tagRegex[tag]))
            .filter((match) => Boolean(match))
            .map((c) => dedent(c[1]));
    }

    if (transComments.length === 0) return;

    if (poEntry.comments.extracted) {
        poEntry.comments.extracted += '\n';
    } else {
        poEntry.comments.extracted = '';
    }
    poEntry.comments.extracted += transComments.join('\n');
}

export function makePotStr(data) {
    return gettextParser.po.compile(data);
}

export function parsePoData(filepath) {
    const poRaw = fs.readFileSync(filepath);
    const parsedPo = gettextParser.po.parse(poRaw.toString());
    const translations = parsedPo.translations;
    const headers = parsedPo.headers;
    return { translations, headers };
}

const pluralRegex = /\splural ?=?([\s\S]*);?/;
export function getPluralFunc(headers) {
    try {
        let pluralFn = pluralRegex.exec(headers['plural-forms'])[1];
        if (pluralFn[pluralFn.length - 1] === ';') {
            pluralFn = pluralFn.slice(0, -1);
        }
        return pluralFn;
    } catch (err) {
        throw new Error(`Failed to parse plural func from headers "${JSON.stringify(headers)}"\n`);
    }
}

export function getNPlurals(headers) {
    const nplurals = /nplurals ?= ?(\d)/.exec(headers['plural-forms'])[1];
    return parseInt(nplurals, 10);
}

export function hasTranslations(translationObj) {
    return translationObj[PO_PRIMITIVES.MSGSTR].reduce((r, t) => r && t.length, true);
}

export function isFuzzy(translationObj) {
    return (
        translationObj && translationObj.comments &&
        translationObj.comments.flag === 'fuzzy');
}

export function pluralFnBody(pluralStr) {
    return `return args[+ (${pluralStr})];`;
}

const fnCache = {};
export function makePluralFunc(pluralStr) {
    /* eslint-disable no-new-func */
    let fn = fnCache[pluralStr];
    if (!fn) {
        fn = new Function('n', 'args', pluralFnBody(pluralStr));
        fnCache[pluralStr] = fn;
    }
    return fn;
}

export function getDefaultPoData(headers) {
    return { headers, translations: { '': {} } };
}

const nonTextRegexp = /\${.*?}|\d|\s|[.,\/#!$%\^&\*;{}=\-_`~()]/g;
export function hasUsefulInfo(text) {
    const withoutExpressions = text.replace(nonTextRegexp, '');
    return Boolean(withoutExpressions.match(/\S/));
}
<commit_msg>fix: string comparison for reference comments (#125)<commit_after>import * as bt from '@babel/types';
import fs from 'fs';
import gettextParser from 'gettext-parser';
import { DEFAULT_HEADERS, PO_PRIMITIVES, LOCATION } from './defaults';
import dedent from 'dedent';

export function buildPotData(translations) {
    const data = {
        charset: 'UTF-8',
        headers: DEFAULT_HEADERS,
        translations: {
            '': {
            },
        },
    };

    for (const trans of translations) {
        const ctx = trans[PO_PRIMITIVES.MSGCTXT] || '';
        if (!data.translations[ctx]) {
            data.translations[ctx] = {};
        }

        if (!data.translations[ctx][trans.msgid]) {
            data.translations[ctx][trans.msgid] = trans;
            continue;
        }

        const oldTrans = data.translations[ctx][trans.msgid];

        // merge references
        if (oldTrans.comments && oldTrans.comments.reference &&
            trans.comments && trans.comments.reference &&
            !oldTrans.comments.reference.includes(trans.comments.reference)) {
            oldTrans.comments.reference = `${oldTrans.comments.reference}\n${trans.comments.reference}`;
        }
    }

    return data;
}

export function applyReference(poEntry, node, filepath, location) {
    if (!poEntry.comments) {
        poEntry.comments = {};
    }

    let reference = null;

    switch (location) {
        case LOCATION.FILE:
            reference = filepath; break;
        case LOCATION.NEVER:
            reference = null; break;
        default:
            reference = `${filepath}:${node.loc.start.line}`;
    }

    poEntry.comments.reference = reference;
    return poEntry;
}

const tagRegex = {};
export function applyExtractedComments(poEntry, nodePath, tag) {
    if (!poEntry.comments) {
        poEntry.comments = {};
    }

    const node = nodePath.node;

    if (!(
        bt.isStatement(node) ||
        bt.isDeclaration(node)
    )) {
        // Collect parents' comments
        //
        applyExtractedComments(poEntry, nodePath.parentPath, tag);
    }

    const comments = node.leadingComments;
    let transComments = comments ? comments.map((c) => c.value) : [];
    if (tag) {
        if (!tagRegex[tag]) {
            tagRegex[tag] = new RegExp(`^\\s*${tag}\\s*(.*?)\\s*$`);
        }
        transComments = transComments
            .map((c) => c.match(tagRegex[tag]))
            .filter((match) => Boolean(match))
            .map((c) => dedent(c[1]));
    }

    if (transComments.length === 0) return;

    if (poEntry.comments.extracted) {
        poEntry.comments.extracted += '\n';
    } else {
        poEntry.comments.extracted = '';
    }
    poEntry.comments.extracted += transComments.join('\n');
}

export function makePotStr(data) {
    return gettextParser.po.compile(data);
}

export function parsePoData(filepath) {
    const poRaw = fs.readFileSync(filepath);
    const parsedPo = gettextParser.po.parse(poRaw.toString());
    const translations = parsedPo.translations;
    const headers = parsedPo.headers;
    return { translations, headers };
}

const pluralRegex = /\splural ?=?([\s\S]*);?/;
export function getPluralFunc(headers) {
    try {
        let pluralFn = pluralRegex.exec(headers['plural-forms'])[1];
        if (pluralFn[pluralFn.length - 1] === ';') {
            pluralFn = pluralFn.slice(0, -1);
        }
        return pluralFn;
    } catch (err) {
        throw new Error(`Failed to parse plural func from headers "${JSON.stringify(headers)}"\n`);
    }
}

export function getNPlurals(headers) {
    const nplurals = /nplurals ?= ?(\d)/.exec(headers['plural-forms'])[1];
    return parseInt(nplurals, 10);
}

export function hasTranslations(translationObj) {
    return translationObj[PO_PRIMITIVES.MSGSTR].reduce((r, t) => r && t.length, true);
}

export function isFuzzy(translationObj) {
    return (
        translationObj && translationObj.comments &&
        translationObj.comments.flag === 'fuzzy');
}

export function pluralFnBody(pluralStr) {
    return `return args[+ (${pluralStr})];`;
}

const fnCache = {};
export function makePluralFunc(pluralStr) {
    /* eslint-disable no-new-func */
    let fn = fnCache[pluralStr];
    if (!fn) {
        fn = new Function('n', 'args', pluralFnBody(pluralStr));
        fnCache[pluralStr] = fn;
    }
    return fn;
}

export function getDefaultPoData(headers) {
    return { headers, translations: { '': {} } };
}

const nonTextRegexp = /\${.*?}|\d|\s|[.,\/#!$%\^&\*;{}=\-_`~()]/g;
export function hasUsefulInfo(text) {
    const withoutExpressions = text.replace(nonTextRegexp, '');
    return Boolean(withoutExpressions.match(/\S/));
}
<|endoftext|>


<commit_before>"use strict";

var async = require('async');

var model_factory = require('./model-factory');

/**
 * Create Repository instance
 *
 * @param {Object} model_schema Model schema
 * @param {Object} worker Redis model worker object
 * @param {Object} redis Redis db client
 *
 * @constructor
 */
function Repository (model_schema, worker, redis) {

    this.model_schema = model_schema;
    worker.model_schema = model_schema;

    bind_redis_to_foreign_keys.call(this);

    /**
     * Get object from redis
     *
     * @param {Number} id Object ID
     * @returns {Promise <Object|Error>}
     */
    this.get = function (id) {
        var self = this;

        return new Promise(function (resolve, reject) {
            return worker.get_model(self.model_schema.key + 'info:' + id).then(get_many_to_many_foreign_values).then((schema) => {
                var result_model = model_factory(schema, model_schema);

                resolve(result_model);
            }).catch(reject);
        });

        function get_many_to_many_foreign_values(schema) {
            var indexes = self.model_schema.indexes;
            var p = [];

            Object.keys(indexes).forEach((field) => {
                if (indexes[field].constructor.name === 'ManyToManyKey') {
                    p.push(indexes[field].get_depended_values(schema.id).then((values) => {
                        schema[field] = values;
                    }));
                }
            });

            return Promise.all(p).then(() => Promise.resolve(schema));
        }
    };

    /**
     * Get many object from redis
     *
     * @param {Array} ids
     * @return {Promise<Array|Error>}
     */
    this.get_many = function (ids) {
        return worker.get_many(ids_to_keys.bind(this)(ids));
    };

    /**
     * Get all objects from redis
     *
     * @return {Promise<Array|Error>}
     */
    this.get_all = function () {
        var self = this;

        return get_all_keys().then(worker.get_many.bind(worker));

        function get_all_keys () {
            return new Promise((resolve, reject) => {
                redis.keys(self.model_schema.key + 'info:' + '*', (err, keys) => {
                    err ? reject(err) : resolve(keys);
                });
            });
        }
    };

    /**
     * Get all object by index name
     *
     * @param {String} indexname Index name
     * @param {Number} id Index value
     *
     * @returns {Promise <Array|Error>}
     */
    this.fetch_by = function (indexname, id) {
        var self = this;

        return new Promise((resolve, reject) => {
            var index = self.model_schema.indexes[indexname];

            index.get_values(id)
                .then(get_models_by_ids.bind(self))
                .then(resolve).catch(reject);
        });

    };

    /**
     * Fetch models by many indexes
     *
     * @param {Array} fetchindexes
     * @returns {Promise <Array|Error>}
     */
    this.fetch_by_many = function (fetchindexes) {
        var self = this;
        var result_keys = [];
        var indexes = self.model_schema.indexes;

        return new Promise((resolve, reject) => {
            get_models_ids().then(get_models_by_ids.bind(self)).then(resolve).catch(reject);
        });

        function get_models_ids () {
            return new Promise((resolve, reject) => {
                async.eachSeries(fetchindexes, (fetchindex, callback) => {
                    indexes[fetchindex.name].get_keys(fetchindex.key_values)
                        .then((keys) => {
                            result_keys = result_keys.concat(keys);

                            callback();
                        }).catch(callback);
                }, (err) => {
                    if (err) return reject(err);

                    redis.sinter(result_keys, (err, ids) => {
                        err ? reject(err) : resolve(ids);
                    });
                });
            });
        }
    };

    /**
     * Save or update model into redis
     *
     * @param {Object|Model} model Model fields object or model
     *
     * @returns {Promise <undefined|Error>}
     */
    this.save = function (model) {
        var self = this;
        var isUpdate = false;

        if (model.constructor.name !== 'Model') {
            model = model_factory(model, this.model_schema);
        }

        return new Promise(function (resolve, reject) {
            model.schema.id = model.hasOwnProperty('id') ? model.schema.id : undefined;

            if (!model.check_fields()) return reject(new Error('Wrong schema'));

            get_id().then(function (id) {

                if (isUpdate) {
                    check_updating_object_existing(id).then(function (isExist) {
                        if (!isExist) return reject(new Error('Updating object not exist'));

                        save_model(id, resolve, reject);

                    }).catch(reject);
                } else {
                    save_model(id, resolve, reject);
                }
            }).catch(reject);
        });

        function get_id() {
            return new Promise(function (resolve, reject) {
                if (!model.id) {
                    redis.incr(self.model_schema.key + 'next_id', function (err, id) {
                        if (err) return reject(err);

                        model.id = id;

                        resolve(id);
                    });
                } else {
                    isUpdate = true;

                    resolve(model.id);
                }
            });
        }

        function save_model(id, resolve, reject) {
            save_or_update(id, model.to_object()).then(function () {
                save_indexes(self.model_schema.indexes).then(function () {
                    resolve(id)
                }).catch(reject);
            }).catch(reject);
        }

        function save_or_update(id, schema) {
            return worker.save_model(self.model_schema.key + 'info:' + id, schema)
        }

        function save_indexes(indexes) {

            return new Promise(function (resolve, reject) {
                async.eachSeries(Object.keys(indexes || []), update_index, function (err) {
                    if (err) return reject(err);

                    resolve();
                });
            });

            function update_index(index, callback) {
                indexes[index].update_key(model.schema[index], model.id)
                    .then(callback, callback);
            }
        }

        function check_updating_object_existing(id) {
            return new Promise(function (resolve, reject) {
                redis.exists(self.model_schema.key + 'info:' + id, function (err, isExist) {
                    if (err) return reject(err);

                    resolve(isExist);
                });
            });

        }
    };

    /**
     * Update object field in db
     *
     * @param {Number} id
     * @param {String} field
     * @param {Number|String|Object} value
     *
     * @return {Promise <undefined|Error>}
     */
    this.update_field = function(id, field, value) {
        return worker.get_model(this.model_schema.key + 'info:' + id)
            .then(schema => {
                return worker.save_field(this.model_schema.key + 'info:' + id, field, value).then(() => {
                    if (this.model_schema.indexes[field]) {
                        return Promise.resolve()
                            .then(() => {
                                return this.model_schema.indexes[field].delete_key(schema[field], id)
                            })
                            .then(() => {
                                return this.model_schema.indexes[field].update_key(value, id)
                            });
                    }

                    return Promise.resolve();
                });
            });
    };

    /**
     * Delete object from redis
     *
     * @param {Number} id Deleting object ID
     * @returns {Promise <undefined|Error>}
     */
    this.delete = function (id) {
        var self = this;

        return new Promise(function (resolve, reject) {
            self.get(id)
                .then(delete_indexes)
                .then(delete_model)
                .then(resolve)
                .catch(reject);
        });

        function delete_indexes (model) {

            var indexes = self.model_schema.indexes;

            return new Promise((resolve, reject) => {
                async.eachSeries(Object.keys(indexes), delete_index, (err) => {
                    err ? reject(err) : resolve(model);
                });

                function delete_index(indexname, callback) {

                    var index = indexes[indexname];
                    var index_value = model[indexname];

                    index.delete_key(index_value, model.id).then(callback, callback);
                }
            });
        }

        function delete_model(model) {
            return worker.delete_model(self.model_schema.key + 'info:' + model.id)
        }
    };

    /**
     * Increment object field by value
     *
     * @param {Number} id Object id
     * @param {String} field Field name
     * @param {Number} value Neg or Pos number
     *
     * @returns {*}
     */
    this.increment_field = function (id, field, value) {
        return worker.increment(this.model_schema.key + 'info:' + id, field, value);
    };

    /**
     * Private functions
     */

    /**
     * Get models by IDs
     *
     * @param {Array} ids
     * @returns {Promise <Array|Error>}
     */
    function get_models_by_ids(ids) {
        var self = this;
        var result = [];

        return new Promise((resolve, reject) => {
            async.eachSeries(ids, get_model, (err) => {
                err ? reject(err) : resolve(result);
            });
        });

        function get_model(id, callback) {
            self.get(id).then((model) => {
                result.push(model);

                callback()
            }).catch(callback);
        }
    }

    /**
     * Bind redis to foreign keys
     */
    function bind_redis_to_foreign_keys() {
        Object.keys(this.model_schema.indexes).forEach((indexname) => {
            this.model_schema.indexes[indexname].redis = redis;
        });
    }

    function ids_to_keys (ids) {
        var keys = [];

        ids.forEach((id) => keys.push(this.model_schema.key + 'info:' + id));

        return keys;
    }

}

module.exports = Repository;<commit_msg>Refactoring fetch by many function and add fetch type<commit_after>"use strict";

var async = require('async'),
    iterate = require('./iterator');

var model_factory = require('./model-factory');

/**
 * Create Repository instance
 *
 * @param {Object} model_schema Model schema
 * @param {Object} worker Redis model worker object
 * @param {Object} redis Redis db client
 *
 * @constructor
 */
function Repository (model_schema, worker, redis) {

    this.model_schema = model_schema;
    worker.model_schema = model_schema;

    bind_redis_to_foreign_keys.call(this);

    /**
     * Get object from redis
     *
     * @param {Number} id Object ID
     * @returns {Promise <Object|Error>}
     */
    this.get = function (id) {
        var self = this;

        return new Promise(function (resolve, reject) {
            return worker.get_model(self.model_schema.key + 'info:' + id).then(get_many_to_many_foreign_values).then((schema) => {
                var result_model = model_factory(schema, model_schema);

                resolve(result_model);
            }).catch(reject);
        });

        function get_many_to_many_foreign_values(schema) {
            var indexes = self.model_schema.indexes;
            var p = [];

            Object.keys(indexes).forEach((field) => {
                if (indexes[field].constructor.name === 'ManyToManyKey') {
                    p.push(indexes[field].get_depended_values(schema.id).then((values) => {
                        schema[field] = values;
                    }));
                }
            });

            return Promise.all(p).then(() => Promise.resolve(schema));
        }
    };

    /**
     * Get many object from redis
     *
     * @param {Array} ids
     * @return {Promise<Array|Error>}
     */
    this.get_many = function (ids) {
        return worker.get_many(ids_to_keys.bind(this)(ids));
    };

    /**
     * Get all objects from redis
     *
     * @return {Promise<Array|Error>}
     */
    this.get_all = function () {
        var self = this;

        return get_all_keys().then(worker.get_many.bind(worker));

        function get_all_keys () {
            return new Promise((resolve, reject) => {
                redis.keys(self.model_schema.key + 'info:' + '*', (err, keys) => {
                    err ? reject(err) : resolve(keys);
                });
            });
        }
    };

    /**
     * Get all object by index name
     *
     * @param {String} indexname Index name
     * @param {Number} id Index value
     *
     * @returns {Promise <Array|Error>}
     */
    this.fetch_by = function (indexname, id) {
        var self = this;

        return new Promise((resolve, reject) => {
            var index = self.model_schema.indexes[indexname];

            index.get_values(id)
                .then(get_models_by_ids.bind(self))
                .then(resolve).catch(reject);
        });

    };

    /**
     * Fetch models by many indexes
     *
     * @param {Array} fetchIndexes
     * @param {String <"i"|"u"|"d">} type
     *
     * @returns {Promise <Array|Error>}
     */
    this.fetch_by_many = function (fetchIndexes, type = "i") {
        var result_keys = [],
            indexes = this.model_schema.indexes;

        return get_models_ids().then(get_models_by_ids.bind(this));

        function get_models_ids () {
            function get_index_keys (index) {
                return indexes[index.name].get_keys(index.key_values)
                    .then(keys => result_keys = result_keys.concat(keys));
            }

            return iterate(fetchIndexes, get_index_keys)
                .then(() => {
                    if (type === "u") {
                        return sunion(result_keys);
                    } else if (type === "d") {
                        return sdiff(result_keys);
                    } else {
                        return sinter(result_keys);
                    }
                });
        }

        function sinter(keys) {
            return new Promise((resolve, reject) => {
                redis.sinter(keys, (err, ids) => {
                    err ? reject(err) : resolve(ids);
                });
            });
        }

        function sunion(keys) {
            return new Promise((resolve, reject) => {
                redis.sunion(keys, (err, ids) => {
                    err ? reject(err) : resolve(ids);
                });
            });
        }

        function sdiff(keys) {
            return new Promise((resolve, reject) => {
                redis.sdiff(keys, (err, ids) => {
                    err ? reject(err) : resolve(ids);
                });
            });
        }
    };

    /**
     * Save or update model into redis
     *
     * @param {Object|Model} model Model fields object or model
     *
     * @returns {Promise <undefined|Error>}
     */
    this.save = function (model) {
        var self = this;
        var isUpdate = false;

        if (model.constructor.name !== 'Model') {
            model = model_factory(model, this.model_schema);
        }

        return new Promise(function (resolve, reject) {
            model.schema.id = model.hasOwnProperty('id') ? model.schema.id : undefined;

            if (!model.check_fields()) return reject(new Error('Wrong schema'));

            get_id().then(function (id) {

                if (isUpdate) {
                    check_updating_object_existing(id).then(function (isExist) {
                        if (!isExist) return reject(new Error('Updating object not exist'));

                        save_model(id, resolve, reject);

                    }).catch(reject);
                } else {
                    save_model(id, resolve, reject);
                }
            }).catch(reject);
        });

        function get_id() {
            return new Promise(function (resolve, reject) {
                if (!model.id) {
                    redis.incr(self.model_schema.key + 'next_id', function (err, id) {
                        if (err) return reject(err);

                        model.id = id;

                        resolve(id);
                    });
                } else {
                    isUpdate = true;

                    resolve(model.id);
                }
            });
        }

        function save_model(id, resolve, reject) {
            save_or_update(id, model.to_object()).then(function () {
                save_indexes(self.model_schema.indexes).then(function () {
                    resolve(id)
                }).catch(reject);
            }).catch(reject);
        }

        function save_or_update(id, schema) {
            return worker.save_model(self.model_schema.key + 'info:' + id, schema)
        }

        function save_indexes(indexes) {

            return new Promise(function (resolve, reject) {
                async.eachSeries(Object.keys(indexes || []), update_index, function (err) {
                    if (err) return reject(err);

                    resolve();
                });
            });

            function update_index(index, callback) {
                indexes[index].update_key(model.schema[index], model.id)
                    .then(callback, callback);
            }
        }

        function check_updating_object_existing(id) {
            return new Promise(function (resolve, reject) {
                redis.exists(self.model_schema.key + 'info:' + id, function (err, isExist) {
                    if (err) return reject(err);

                    resolve(isExist);
                });
            });

        }
    };

    /**
     * Update object field in db
     *
     * @param {Number} id
     * @param {String} field
     * @param {Number|String|Object} value
     *
     * @return {Promise <undefined|Error>}
     */
    this.update_field = function(id, field, value) {
        return worker.get_model(this.model_schema.key + 'info:' + id)
            .then(schema => {
                return worker.save_field(this.model_schema.key + 'info:' + id, field, value).then(() => {
                    if (this.model_schema.indexes[field]) {
                        return Promise.resolve()
                            .then(() => {
                                return this.model_schema.indexes[field].delete_key(schema[field], id)
                            })
                            .then(() => {
                                return this.model_schema.indexes[field].update_key(value, id)
                            });
                    }

                    return Promise.resolve();
                });
            });
    };

    /**
     * Delete object from redis
     *
     * @param {Number} id Deleting object ID
     * @returns {Promise <undefined|Error>}
     */
    this.delete = function (id) {
        var self = this;

        return new Promise(function (resolve, reject) {
            self.get(id)
                .then(delete_indexes)
                .then(delete_model)
                .then(resolve)
                .catch(reject);
        });

        function delete_indexes (model) {

            var indexes = self.model_schema.indexes;

            return new Promise((resolve, reject) => {
                async.eachSeries(Object.keys(indexes), delete_index, (err) => {
                    err ? reject(err) : resolve(model);
                });

                function delete_index(indexname, callback) {

                    var index = indexes[indexname];
                    var index_value = model[indexname];

                    index.delete_key(index_value, model.id).then(callback, callback);
                }
            });
        }

        function delete_model(model) {
            return worker.delete_model(self.model_schema.key + 'info:' + model.id)
        }
    };

    /**
     * Increment object field by value
     *
     * @param {Number} id Object id
     * @param {String} field Field name
     * @param {Number} value Neg or Pos number
     *
     * @returns {*}
     */
    this.increment_field = function (id, field, value) {
        return worker.increment(this.model_schema.key + 'info:' + id, field, value);
    };

    /**
     * Private functions
     */

    /**
     * Get models by IDs
     *
     * @param {Array} ids
     * @returns {Promise <Array|Error>}
     */
    function get_models_by_ids(ids) {
        var self = this;
        var result = [];

        return new Promise((resolve, reject) => {
            async.eachSeries(ids, get_model, (err) => {
                err ? reject(err) : resolve(result);
            });
        });

        function get_model(id, callback) {
            self.get(id).then((model) => {
                result.push(model);

                callback()
            }).catch(callback);
        }
    }

    /**
     * Bind redis to foreign keys
     */
    function bind_redis_to_foreign_keys() {
        Object.keys(this.model_schema.indexes).forEach((indexname) => {
            this.model_schema.indexes[indexname].redis = redis;
        });
    }

    function ids_to_keys (ids) {
        var keys = [];

        ids.forEach((id) => keys.push(this.model_schema.key + 'info:' + id));

        return keys;
    }

}

module.exports = Repository;<|endoftext|>


<commit_before>"use strict";
var _compat = require("./node06compat")
	, _fs = require("fs")
	, _path = require("path");

module.exports = (function(){

	var _verifyDirectory = function(path) {
		var stats;

		if (typeof path === "undefined"
			|| (typeof path === "string" && path.trim().length === 0)) {
			throw new Error("The path must be provided when instantiating the object.");
		}

		// Resolve the full path so that if an error occurs
		// we can show the user the absolute path, for clarity.
		path = _path.resolve(path);

		try {
			stats = _fs.statSync(path);
		} catch (e) {
			throw new Error("The directory path does not exist. [" + path + "]");
		}

		if (!stats.isDirectory()){
			throw new Error("The path provided is not a directory. [" + path + "]");
		}
	};

	var _importFiles = function(path, files){
		var moduleList = []
			, relativePath = _path.resolve(_path.join(process.cwd(), path))
			, trimmedName
			, module
			, obj = {};

		files.forEach(function (element, index, array){
			if (_fs.lstatSync(_path.join(path, element)).isFile() && element.substring(0,1) !== "."){
				trimmedName =  element.substring(0, (element.length - 3));
				module = require(_path.join(relativePath, trimmedName));
				moduleList.push(module);
				obj[trimmedName] = module;
			}
		});

		obj.length = moduleList.length;
		obj.toArray = function(){return moduleList;};

		return obj;
	};

	return function(path){
		_verifyDirectory(path);

		var fileList = []
			, modules = [];

		var files = _fs.readdirSync(path);

		return _importFiles(path, files);
	};
}());<commit_msg>Use path.basename() and path.extname() instead of assuming that the file has a 3 character extension and using substr.<commit_after>"use strict";
var _compat = require("./node06compat")
	, _fs = require("fs")
	, _path = require("path");

module.exports = (function(){

	var _verifyDirectory = function(path) {
		var stats;

		if (typeof path === "undefined"
			|| (typeof path === "string" && path.trim().length === 0)) {
			throw new Error("The path must be provided when instantiating the object.");
		}

		// Resolve the full path so that if an error occurs
		// we can show the user the absolute path, for clarity.
		path = _path.resolve(path);

		try {
			stats = _fs.statSync(path);
		} catch (e) {
			throw new Error("The directory path does not exist. [" + path + "]");
		}

		if (!stats.isDirectory()){
			throw new Error("The path provided is not a directory. [" + path + "]");
		}
	};

	var _importFiles = function(path, files){
		var moduleList = []
			, relativePath = _path.resolve(_path.join(process.cwd(), path))
			, trimmedName
			, module
			, obj = {};

		files.forEach(function (element, index, array){
			// Require each file, skipping dotfiles.
			if (_fs.lstatSync(_path.join(path, element)).isFile() && element.substring(0,1) !== "."){
				trimmedName = _path.basename(element, _path.extname(element));
				module = require(_path.join(relativePath, trimmedName));
				moduleList.push(module);
				obj[trimmedName] = module;
			}
		});

		obj.length = moduleList.length;
		obj.toArray = function(){return moduleList;};

		return obj;
	};

	return function(path){
		_verifyDirectory(path);

		var fileList = []
			, modules = [];

		var files = _fs.readdirSync(path);

		return _importFiles(path, files);
	};
}());<|endoftext|>


<commit_before>require('enyo');

var
	Dom = require('./dom');

var _baseScreenType = 'standard',
	_riRatio,
	_screenType,
	_screenTypes = [ {name: 'standard', pxPerRem: 16, width: global.innerWidth,  height: global.innerHeight, aspectRatioName: 'standard'} ],	// Assign one sane value in case defineScreenTypes is never run.
	_screenTypeObject,
	_oldScreenType;

var getScreenTypeObject = function (type) {
	type = type || _screenType;
	if (_screenTypeObject && _screenTypeObject.name == type) {
		return _screenTypeObject;
	}
	return _screenTypes.filter(function (elem) {
		return (type == elem.name);
	})[0];
};

/**
* Resolution independence methods
* @module enyo/resolution
*/
var ri = module.exports = {
	/**
	* Setup screen resolution scaling capabilities by defining all of the screens you're working
	* with. These should be in the order of smallest to largest (according to width). Running
	* this also initializes the rest of this resolution code.
	*
	* In the arguments, the following properties are required: 'name', 'pxPerRem', 'width',
	* 'aspectRatioName'. The property 'base' defines the primary or default resoultion that
	* everything else will be based upon.
	*
	* ```
	* ri.defineScreenTypes([
	* 	{name: 'vga',     pxPerRem: 8,  width: 640,  height: 480,  aspectRatioName: 'standard'},
	* 	{name: 'xga',     pxPerRem: 16, width: 1024, height: 768,  aspectRatioName: 'standard'},
	* 	{name: 'hd',      pxPerRem: 16, width: 1280, height: 720,  aspectRatioName: 'hdtv'},
	* 	{name: 'fhd',     pxPerRem: 24, width: 1920, height: 1080, aspectRatioName: 'hdtv', base: true},
	* 	{name: 'uw-uxga', pxPerRem: 24, width: 2560, height: 1080, aspectRatioName: 'cinema'},
	* 	{name: 'uhd',     pxPerRem: 48, width: 3840, height: 2160, aspectRatioName: 'hdtv'}
	* ]);
	* ```
	*
	* @param {Array} types An array of objects with arguments like the example
	* @public
	*/
	defineScreenTypes: function (types) {
		_screenTypes = types;
		for (var i = 0; i < _screenTypes.length; i++) {
			if (_screenTypes[i]['base']) _baseScreenType = _screenTypes[i].name;
		}
		ri.init();
	},

	/**
	* Fetches the best-matching screen type name for the current screen size. The "best" screen type
	* is determined by the screen type name that is the closest to the screen resolution without
	* going over. ("The Price is Right" style.)
	*
	* @param {Object} [rez] - Optional measurement scheme. Must have "height" and "width" properties.
	* @returns {String} Screen type, like "fhd", "uhd", etc.
	* @public
	*/
	getScreenType: function (rez) {
		rez = rez || {
			height: global.innerHeight,
			width: global.innerWidth
		};
		var i,
			types = _screenTypes,
			bestMatch = types[types.length - 1].name;

		// loop thorugh resolutions
		for (i = types.length - 1; i >= 0; i--) {
			// find the one that matches our current size or is smaller. default to the first.
			if (rez.width <= types[i].width) {
				bestMatch = types[i].name;
			}
		}
		// return the name of the resolution if we find one.
		return bestMatch;
	},

	/**
	* @private
	*/
	updateScreenBodyClasses: function (type) {
		type = type || _screenType;
		if (_oldScreenType) {
			Dom.removeClass(document.body, 'enyo-res-' + _oldScreenType.toLowerCase());
			var oldScrObj = getScreenTypeObject(_oldScreenType);
			if (oldScrObj && oldScrObj.aspectRatioName) {
				Dom.removeClass(document.body, 'enyo-aspect-ratio-' + oldScrObj.aspectRatioName.toLowerCase());
			}
		}
		if (type) {
			Dom.addBodyClass('enyo-res-' + type.toLowerCase());
			var scrObj = getScreenTypeObject(type);
			if (scrObj.aspectRatioName) {
				Dom.addBodyClass('enyo-aspect-ratio-' + scrObj.aspectRatioName.toLowerCase());
			}
			return type;
		}
	},

	/**
	* @private
	*/
	getRiRatio: function (type) {
		type = type || _screenType;
		if (type) {
			var ratio = this.getUnitToPixelFactors(type) / this.getUnitToPixelFactors(_baseScreenType);
			if (type == _screenType) {
				// cache this if it's for our current screen type.
				_riRatio = ratio;
			}
			return ratio;
		}
		return 1;
	},

	/**
	* @private
	*/
	getUnitToPixelFactors: function (type) {
		type = type || _screenType;
		if (type) {
			return getScreenTypeObject(type).pxPerRem;
		}
		return 1;
	},

	/**
	* Calculates the aspect ratio of the screen type provided. If none is provided the current
	* screen type is used.
	*
	* @param {String} type Screen type to get the aspect ratio of. Providing nothing uses the
	*	current screen type.
	* @returns {Number} The calculated screen ratio (1.333, 1.777, 2.333, etc)
	* @public
	*/
	getAspectRatio: function (type) {
		var scrObj = getScreenTypeObject(type);
		if (scrObj.width && scrObj.height) {
			return (scrObj.width / scrObj.height);
		}
		return 1;
	},

	/**
	* Returns the name of the aspect ration given the screen type or the default screen type if
	* none is proided.
	*
	* @param {String} type Screen type to get the aspect ratio of. Providing nothing uses the
	*	current screen type.
	* @returns {String} The name of the type of screen ratio
	* @public
	*/
	getAspectRatioName: function (type) {
		var scrObj = getScreenTypeObject(type);
		 return scrObj.aspectRatioName || 'standard';
	},

	/**
	* Takes a provided pixel value and preforms a scaling operation on the number based on the
	* current screen type.
	*
	* @param {Number} px The amount of standard-resolution pixels to scale to the current screen
	*	resolution.
	* @returns {Number} The scaled value based on the current screen scaling factor.
	* @public
	*/
	scale: function (px) {
		return (_riRatio || this.getRiRatio()) * px;
	},

	/**
	* The default configurable [options]{@link ri.selectSrc#options}.
	*
	* @typedef {Object} ri.selectSrc~src
	* @property {String} hd - HD / 720p Resolution image asset source URI/URL
	* @property {String} fhd - FHD / 1080p Resolution image asset source URI/URL
	* @property {String} uhd - UHD / 4K Resolution image asset source URI/URL
	*
	* @typedef {String} ri.selectSrc~src - Image asset source URI/URL
	*/

	/**
	* Image src chooser. A simple utility method to select the ideal image asset from a set of
	* assets, based on various screen resolutions: HD (720p), FHD (1080p), UHD (4k). When provided
	* with a src argument, multiResSrc will choose the best image with respect to the current screen
	* resolution. `src` may be either the traditional string, which will pass straight through, or a
	* hash/object of screen types and their asset sources (keys:screen and values:src). The image
	* sources will be used chosen when the screen resolution is less than or equal to the provided
	* screen types.
	*
	* ```
	* // Take advantage of the multi-rez mode
	* {kind: 'moon.Image', src: {
	* 	'hd': 'http://lorempixel.com/64/64/city/1/',
	* 	'fhd': 'http://lorempixel.com/128/128/city/1/',
	* 	'uhd': 'http://lorempixel.com/256/256/city/1/'
	* }, alt: 'Multi-rez'},
	* // Standard string `src`
	* {kind: 'moon.Image', src: http://lorempixel.com/128/128/city/1/', alt: 'Large'},
	* ```
	*
	* @param {(String|moon.ri.selectSrc~src)} src A string containing a single image src or a
	*	key/value hash/object containing keys representing screen types (hd, fhd, uhd, etc) and
	*	values containing the asset src for that target screen resolution.
	* @returns {String} The choosen src given the string or list provided.
	* @public
	*/
	selectSrc: function (src) {
		if (typeof src != 'string' && src) {
			var i, t,
				newSrc = src.fhd || src.uhd || src.hd,
				types = _screenTypes;

			// loop through resolutions
			for (i = types.length - 1; i >= 0; i--) {
				t = types[i].name;
				if (_screenType == t && src[t]) newSrc = src[t];
			}

			src = newSrc;
		}
		return src;
	},

	/**
	* This will need to be re-run any time the screen size changes, so all the values can be
	* re-cached.
	*
	* @public
	*/
	// Later we can wire this up to a screen resize event so it doesn't need to be called manually.
	init: function () {
		_oldScreenType = _screenType;
		_screenType = this.getScreenType();
		_screenTypeObject = getScreenTypeObject();
		this.updateScreenBodyClasses();
		Dom.unitToPixelFactors.rem = this.getUnitToPixelFactors();
		_riRatio = this.getRiRatio();
	}
};

ri.init();
<commit_msg>JSDoc cleanup Enyo-DCO-1.1-Signed-Off-By: Jim Tang (jim.tang@lge.com)<commit_after>require('enyo');

var
	Dom = require('./dom');

var _baseScreenType = 'standard',
	_riRatio,
	_screenType,
	_screenTypes = [ {name: 'standard', pxPerRem: 16, width: global.innerWidth,  height: global.innerHeight, aspectRatioName: 'standard'} ],	// Assign one sane value in case defineScreenTypes is never run.
	_screenTypeObject,
	_oldScreenType;

var getScreenTypeObject = function (type) {
	type = type || _screenType;
	if (_screenTypeObject && _screenTypeObject.name == type) {
		return _screenTypeObject;
	}
	return _screenTypes.filter(function (elem) {
		return (type == elem.name);
	})[0];
};

/**
* Resolution independence methods
* @module enyo/resolution
*/
var ri = module.exports = {
	/**
	* Sets up screen resolution scaling capabilities by defining an array of all the screens
	* being used. These should be listed in order from smallest to largest, according to
	* width.
	*
	* The `name`, `pxPerRem`, `width`, and `aspectRatioName` properties are required for
	* each screen type in the array. Setting `base: true` on a screen type marks it as the
	* default resolution, upon which everything else will be based.
	*
	* Executing this method also initializes the rest of the resolution-independence code.
	*
	* ```
	* ri.defineScreenTypes([
	* 	{name: 'vga',     pxPerRem: 8,  width: 640,  height: 480,  aspectRatioName: 'standard'},
	* 	{name: 'xga',     pxPerRem: 16, width: 1024, height: 768,  aspectRatioName: 'standard'},
	* 	{name: 'hd',      pxPerRem: 16, width: 1280, height: 720,  aspectRatioName: 'hdtv'},
	* 	{name: 'fhd',     pxPerRem: 24, width: 1920, height: 1080, aspectRatioName: 'hdtv', base: true},
	* 	{name: 'uw-uxga', pxPerRem: 24, width: 2560, height: 1080, aspectRatioName: 'cinema'},
	* 	{name: 'uhd',     pxPerRem: 48, width: 3840, height: 2160, aspectRatioName: 'hdtv'}
	* ]);
	* ```
	*
	* @param {Array} types - An array of objects containing screen configuration data, as in the
	* preceding example.
	* @public
	*/
	defineScreenTypes: function (types) {
		_screenTypes = types;
		for (var i = 0; i < _screenTypes.length; i++) {
			if (_screenTypes[i]['base']) _baseScreenType = _screenTypes[i].name;
		}
		ri.init();
	},

	/**
	* Fetches the name of the screen type that best matches the current screen size. The best
	* match is defined as the screen type that is the closest to the screen resolution without
	* going over. ("The Price is Right" style.)
	*
	* @param {Object} [rez] - Optional measurement scheme. Must include `height` and `width` properties.
	* @returns {String} Screen type (e.g., `'fhd'`, `'uhd'`, etc.)
	* @public
	*/
	getScreenType: function (rez) {
		rez = rez || {
			height: global.innerHeight,
			width: global.innerWidth
		};
		var i,
			types = _screenTypes,
			bestMatch = types[types.length - 1].name;

		// loop thorugh resolutions
		for (i = types.length - 1; i >= 0; i--) {
			// find the one that matches our current size or is smaller. default to the first.
			if (rez.width <= types[i].width) {
				bestMatch = types[i].name;
			}
		}
		// return the name of the resolution if we find one.
		return bestMatch;
	},

	/**
	* @private
	*/
	updateScreenBodyClasses: function (type) {
		type = type || _screenType;
		if (_oldScreenType) {
			Dom.removeClass(document.body, 'enyo-res-' + _oldScreenType.toLowerCase());
			var oldScrObj = getScreenTypeObject(_oldScreenType);
			if (oldScrObj && oldScrObj.aspectRatioName) {
				Dom.removeClass(document.body, 'enyo-aspect-ratio-' + oldScrObj.aspectRatioName.toLowerCase());
			}
		}
		if (type) {
			Dom.addBodyClass('enyo-res-' + type.toLowerCase());
			var scrObj = getScreenTypeObject(type);
			if (scrObj.aspectRatioName) {
				Dom.addBodyClass('enyo-aspect-ratio-' + scrObj.aspectRatioName.toLowerCase());
			}
			return type;
		}
	},

	/**
	* @private
	*/
	getRiRatio: function (type) {
		type = type || _screenType;
		if (type) {
			var ratio = this.getUnitToPixelFactors(type) / this.getUnitToPixelFactors(_baseScreenType);
			if (type == _screenType) {
				// cache this if it's for our current screen type.
				_riRatio = ratio;
			}
			return ratio;
		}
		return 1;
	},

	/**
	* @private
	*/
	getUnitToPixelFactors: function (type) {
		type = type || _screenType;
		if (type) {
			return getScreenTypeObject(type).pxPerRem;
		}
		return 1;
	},

	/**
	* Calculates the aspect ratio of the specified screen type. If no screen type is provided,
	* the current screen type is used.
	*
	* @param {String} type - Screen type whose aspect ratio will be calculated. If no screen
	* type is provided, the current screen type is used.
	* @returns {Number} The calculated screen ratio (e.g., `1.333`, `1.777`, `2.333`, etc.)
	* @public
	*/
	getAspectRatio: function (type) {
		var scrObj = getScreenTypeObject(type);
		if (scrObj.width && scrObj.height) {
			return (scrObj.width / scrObj.height);
		}
		return 1;
	},

	/**
	* Returns the name of the aspect ratio for a specified screen type, or for the default
	* screen type if none is provided.
	*
	* @param {String} type - Screen type whose aspect ratio name will be returned. If no
	* screen type is provided, the current screen type will be used.
	* @returns {String} The name of the screen type's aspect ratio
	* @public
	*/
	getAspectRatioName: function (type) {
		var scrObj = getScreenTypeObject(type);
		 return scrObj.aspectRatioName || 'standard';
	},

	/**
	* Takes a provided pixel value and performs a scaling operation based on the current
	* screen type.
	*
	* @param {Number} px - The quantity of standard-resolution pixels to scale to the
	* current screen resolution.
	* @returns {Number} The scaled value based on the current screen scaling factor
	* @public
	*/
	scale: function (px) {
		return (_riRatio || this.getRiRatio()) * px;
	},

	/**
	* The default configurable [options]{@link ri.selectSrc#options}.
	*
	* @typedef {Object} ri.selectSrc~src
	* @property {String} hd - HD / 720p Resolution image asset source URI/URL
	* @property {String} fhd - FHD / 1080p Resolution image asset source URI/URL
	* @property {String} uhd - UHD / 4K Resolution image asset source URI/URL
	*
	* @typedef {String} ri.selectSrc~src - Image asset source URI/URL
	*/

	/**
	* Selects the ideal image asset from a set of assets, based on various screen
	* resolutions: HD (720p), FHD (1080p), UHD (4k). When a `src` argument is
	* provided, `selectSrc()` will choose the best image with respect to the current
	* screen resolution. `src` may be either the traditional string, which will pass
	* straight through, or a hash/object of screen types and their asset sources
	* (keys:screen and values:src). The image sources will be used when the screen
	* resolution is less than or equal to the provided screen types.
	*
	* ```
	* // Take advantage of the multi-res mode
	* var
	* 	kind = require('enyo/kind'),
	* 	Image = require('enyo/Image');
	*
	* {kind: Image, src: {
	* 	'hd': 'http://lorempixel.com/64/64/city/1/',
	* 	'fhd': 'http://lorempixel.com/128/128/city/1/',
	* 	'uhd': 'http://lorempixel.com/256/256/city/1/'
	* }, alt: 'Multi-res'},
	*
	* // Standard string `src`
	* {kind: Image, src: http://lorempixel.com/128/128/city/1/', alt: 'Large'},
	* ```
	*
	* @param {(String|module:enyo/resolution#selectSrc~src)} src - A string containing
	* a single image source or a key/value hash/object containing keys representing screen
	* types (`'hd'`, `'fhd'`, `'uhd'`, etc.) and values containing the asset source for
	* that target screen resolution.
	* @returns {String} The chosen source, given the string or list provided
	* @public
	*/
	selectSrc: function (src) {
		if (typeof src != 'string' && src) {
			var i, t,
				newSrc = src.fhd || src.uhd || src.hd,
				types = _screenTypes;

			// loop through resolutions
			for (i = types.length - 1; i >= 0; i--) {
				t = types[i].name;
				if (_screenType == t && src[t]) newSrc = src[t];
			}

			src = newSrc;
		}
		return src;
	},

	/**
	* This will need to be re-run any time the screen size changes, so all the values can be
	* re-cached.
	*
	* @public
	*/
	// Later we can wire this up to a screen resize event so it doesn't need to be called manually.
	init: function () {
		_oldScreenType = _screenType;
		_screenType = this.getScreenType();
		_screenTypeObject = getScreenTypeObject();
		this.updateScreenBodyClasses();
		Dom.unitToPixelFactors.rem = this.getUnitToPixelFactors();
		_riRatio = this.getRiRatio();
	}
};

ri.init();
<|endoftext|>