<commit_before>/* eslint-disable no-underscore-dangle */

import {stripIndent} from 'common-tags'
import expect from 'expect.js'
import {create} from '../../src'
import DomRenderer from '../../src/backends/DomRenderer'
import {
  generateClassName,
  computeStyle,
  getStyle,
  getCss,
  getRules,
  removeWhitespace
} from '../utils'

const settings = {generateClassName}

describe('Functional: sheet', () => {
  let jss

  beforeEach(() => {
    jss = create(settings)
  })

  describe('sheet.attach() CSS check from DOM', () => {
    function check(styles, options) {
      const sheet = jss.createStyleSheet(styles, options).attach()
      const style = getStyle()
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
      sheet.detach()
    }

    it('should render simple sheet', () => {
      check({a: {width: '1px', float: 'left'}})
    })

    it('should render sheet with media query', () => {
      check({
        a: {color: 'red'},
        '@media (min-width: 1024px)': {
          a: {color: 'blue'},
          b: {color: 'white'}
        },
        '@media (min-width: 100px)': {
          a: {color: 'green'}
        }
      })
    })
  })

  describe('sheet.attach() and sheet.detach()', () => {
    let sheet
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have attached the sheet', () => {
      expect(sheet.attached).to.be(true)
      expect(style).to.be.an(Element)
      expect(style.parentNode).to.be.an(Element)
    })

    it('should have rendered nothing inside', () => {
      expect(style.innerHTML.trim()).to.be(sheet.toString().trim())
    })

    it('should detach sheet', () => {
      sheet.detach()
      expect(sheet.attached).to.be(false)
      expect(style.parentNode).to.be(null)
    })
  })

  describe('Options: {media, meta}', () => {
    let sheet
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet(null, {media: 'screen', meta: 'test'}).attach()
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have right options', () => {
      expect(sheet.options.media).to.be('screen')
      expect(sheet.options.meta).to.be('test')
    })

    it('should have rendered attributes', () => {
      expect(style.getAttribute('media'), 'screen')
      expect(style.getAttribute('data-meta'), 'test')
    })
  })

  describe('Option: {link: true}', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({a: {float: 'left'}}, {link: true}).attach()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should link the DOM node', () => {
      expect(sheet.getRule('a').renderable).to.be.a(CSSStyleRule)
    })

    it('should link the DOM node to added rule', () => {
      sheet.addRule('b', {color: 'red'})
      expect(sheet.getRule('b').renderable).to.be.a(CSSStyleRule)
    })
  })

  describe('Option {virtual: true}', () => {
    it('should not render style', () => {
      const sheet = jss.createStyleSheet({a: {float: 'left'}}, {virtual: true})
      sheet.attach()
      expect(getStyle()).to.be(undefined)
      sheet.detach()
    })
  })

  describe('Option {element}', () => {
    it('should render style using provided element', () => {
      const element = document.createElement('style')
      element.type = 'text/css'
      const sheet = jss.createStyleSheet({a: {float: 'left'}}, {element})
      sheet.attach()
      expect(getCss(element)).to.be(removeWhitespace(sheet.toString()))
      sheet.detach()
    })
  })

  describe('Option: {index}', () => {
    it('should be 0 by default', () => {
      const sheet = jss.createStyleSheet({})
      expect(sheet.options.index).to.be(0)
    })

    it('should be set by the options argument', () => {
      [-50, 0, 50, 9999].forEach((n) => {
        const sheet2 = jss.createStyleSheet({}, {index: n})
        expect(sheet2.options.index).to.be(n)
      })
    })
  })

  describe('.addRule()', () => {
    let sheet
    let rule
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      rule = sheet.addRule('a', {float: 'left'})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render only 1 rule', () => {
      expect(getRules(style).length).to.be(1)
    })

    it('should render correct CSS', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })

    it('should register the rule', () => {
      expect(sheet.getRule('a')).to.be(rule)
    })

    it('should add a rule to a detached sheet', () => {
      sheet.detach()
      const newRule = sheet.addRule('b', {float: 'right'})
      sheet.attach()
      expect(sheet.getRule('b')).to.be(newRule)
    })

    it('should link sheet in rules options', () => {
      expect(sheet.getRule('a').options.sheet).to.be(sheet)
    })
  })

  describe('.addRule() with .addRule() call within a plugin and attached sheet', () => {
    let style
    let sheet

    beforeEach(() => {
      function addRule(rule) {
        if (rule.name === 'a') {
          rule.options.sheet.addRule('b', {color: 'red'})
        }
      }
      const localJss = create(settings).use(addRule)
      sheet = localJss.createStyleSheet().attach()
      sheet.addRule('a', {color: 'green'})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render rules in the right order', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with @media and attached sheet', () => {
    let style
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('a', {color: 'red'})
      // It is important to use exactly this query, because
      // IE will add "all" always when `cssRules.insertRule` is used,
      // however all others will always remove "all" if query contains a second
      // condition.
      sheet.addRule('@media all', {
        a: {color: 'green'}
      })
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render @media', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with empty styles', () => {
    let sheet
    let style
    let warned = false

    beforeEach(() => {
      DomRenderer.__Rewire__('warning', () => {
        warned = true
      })
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('a', {})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
      DomRenderer.__ResetDependency__('warning')
    })

    it('should not render', () => {
      expect(getCss(style)).to.be('')
    })

    it('should not warn', () => {
      expect(warned).to.be(false)
    })
  })

  describe('.addRule() with @keyframes and attached sheet', () => {
    const isSupported = 'animationName' in document.body.style
    let style
    let sheet

    // We skip this test as keyframes are not supported by browser.
    if (!isSupported) return

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('@keyframes id', {
        '0%': {top: '0px'}
      })
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render @keyframes', () => {
      // Safari adds the prefix automatically.
      const css = getCss(style).replace('-webkit-', '')
      expect(css).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with invalid decl to attached sheet', () => {
    let warned = false
    let sheet

    beforeEach(() => {
      DomRenderer.__Rewire__('warning', () => {
        warned = true
      })
    })

    afterEach(() => {
      DomRenderer.__ResetDependency__('warning')
      sheet.detach()
    })

    it('should not throw', () => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('%%%%', {color: 'red'})
      expect(warned).to.be(true)
    })
  })

  describe('.addRules() with an attached sheet', () => {
    let sheet
    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {width: '1px'}
      }).attach()
      sheet.addRules({
        b: {
          width: '2px'
        }
      })
    })

    it('should add rules to an attached sheet', () => {
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(computeStyle(sheet.classes.b).width).to.be('2px')
    })

    it('should keep the values after reattaching', () => {
      sheet.detach().attach()
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(computeStyle(sheet.classes.b).width).to.be('2px')
    })
  })

  describe('.deleteRule()', () => {
    it('should delete a rule from the sheet and DOM', () => {
      const sheet = jss.createStyleSheet(
        {a: {width: '1px'}},
        {link: true}
      ).attach()
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(sheet.deleteRule('a')).to.be(true)
      expect(sheet.getRule('a')).to.be(undefined)
      expect(computeStyle(sheet.classes.a).width).not.to.be('1px')
      sheet.detach()
    })
  })

  describe('rule.prop()', () => {
    let rule
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({a: {color: 'green'}}, {link: true})
      rule = sheet.getRule('a')
      sheet.attach()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have initial color', () => {
      expect(rule.style.color).to.be('green')
    })

    it('should apply a style prop', () => {
      rule.prop('color', 'red')
      expect(rule.prop('color')).to.be('red')
    })

    it('should cache the new prop', () => {
      rule.prop('color', 'red')
      expect(rule.style.color).to.be('red')
    })

    it('should apply new prop to the DOM', () => {
      rule.prop('color', 'red')
      expect(rule.renderable.style.color).to.be('red')
    })
  })

  describe('rule.selector', () => {
    let sheet
    let rule

    beforeEach(() => {
      sheet = jss.createStyleSheet(
        {a: {width: '1px'}},
        {link: true}
      ).attach()
      rule = sheet.getRule('a')
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should set the selector', () => {
      rule.selector = '.test'
      expect(rule.selector).to.be('.test')
    })

    it('should apply selector to the DOM', () => {
      rule.selector = '.test'
      expect(computeStyle('test').width).to.be('1px')
    })

    it('should register the class name in sheet.classes', () => {
      expect(sheet.classes.a, 'test')
    })
  })

  describe('sheet.update()', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {
          color: theme => theme.color
        },
        '@media all': {
          b: {
            color: theme => theme.color
          }
        }
      }, {link: true})
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should return correct .toString()', () => {
      expect(sheet.toString()).to.be('')

      sheet.update({
        color: 'green'
      })

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: green;
        }
        @media all {
          .b-id {
            color: green;
          }
        }
      `)

      sheet.update({
        color: 'yellow'
      })

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: yellow;
        }
        @media all {
          .b-id {
            color: yellow;
          }
        }
      `)
    })

    it('should render sheet with updated props', () => {
      sheet.update({color: 'green'}).attach()
      expect(getCss(getStyle())).to.be(removeWhitespace(sheet.toString()))
    })

    it('should update specific rule', () => {
      sheet.update({color: 'yellow'})
      sheet.update('a', {color: 'green'})

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: green;
        }
        @media all {
          .b-id {
            color: yellow;
          }
        }
      `)
    })

    it('should render updated rule', () => {
      sheet.update('a', {color: 'green'}).attach()
      expect(getCss(getStyle())).to.be(removeWhitespace(sheet.toString()))
    })
  })
})
<commit_msg>Fix sheet.update() test, add test for .addRule with function value<commit_after>/* eslint-disable no-underscore-dangle */

import {stripIndent} from 'common-tags'
import expect from 'expect.js'
import {create} from '../../src'
import DomRenderer from '../../src/backends/DomRenderer'
import {
  generateClassName,
  computeStyle,
  getStyle,
  getCss,
  getRules,
  removeWhitespace
} from '../utils'

const settings = {generateClassName}

describe('Functional: sheet', () => {
  let jss

  beforeEach(() => {
    jss = create(settings)
  })

  describe('sheet.attach() CSS check from DOM', () => {
    function check(styles, options) {
      const sheet = jss.createStyleSheet(styles, options).attach()
      const style = getStyle()
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
      sheet.detach()
    }

    it('should render simple sheet', () => {
      check({a: {width: '1px', float: 'left'}})
    })

    it('should render sheet with media query', () => {
      check({
        a: {color: 'red'},
        '@media (min-width: 1024px)': {
          a: {color: 'blue'},
          b: {color: 'white'}
        },
        '@media (min-width: 100px)': {
          a: {color: 'green'}
        }
      })
    })
  })

  describe('sheet.attach() and sheet.detach()', () => {
    let sheet
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have attached the sheet', () => {
      expect(sheet.attached).to.be(true)
      expect(style).to.be.an(Element)
      expect(style.parentNode).to.be.an(Element)
    })

    it('should have rendered nothing inside', () => {
      expect(style.innerHTML.trim()).to.be(sheet.toString().trim())
    })

    it('should detach sheet', () => {
      sheet.detach()
      expect(sheet.attached).to.be(false)
      expect(style.parentNode).to.be(null)
    })
  })

  describe('Options: {media, meta}', () => {
    let sheet
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet(null, {media: 'screen', meta: 'test'}).attach()
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have right options', () => {
      expect(sheet.options.media).to.be('screen')
      expect(sheet.options.meta).to.be('test')
    })

    it('should have rendered attributes', () => {
      expect(style.getAttribute('media'), 'screen')
      expect(style.getAttribute('data-meta'), 'test')
    })
  })

  describe('Option: {link: true}', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({a: {float: 'left'}}, {link: true}).attach()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should link the DOM node', () => {
      expect(sheet.getRule('a').renderable).to.be.a(CSSStyleRule)
    })

    it('should link the DOM node to added rule', () => {
      sheet.addRule('b', {color: 'red'})
      expect(sheet.getRule('b').renderable).to.be.a(CSSStyleRule)
    })
  })

  describe('Option {virtual: true}', () => {
    it('should not render style', () => {
      const sheet = jss.createStyleSheet({a: {float: 'left'}}, {virtual: true})
      sheet.attach()
      expect(getStyle()).to.be(undefined)
      sheet.detach()
    })
  })

  describe('Option {element}', () => {
    it('should render style using provided element', () => {
      const element = document.createElement('style')
      element.type = 'text/css'
      const sheet = jss.createStyleSheet({a: {float: 'left'}}, {element})
      sheet.attach()
      expect(getCss(element)).to.be(removeWhitespace(sheet.toString()))
      sheet.detach()
    })
  })

  describe('Option: {index}', () => {
    it('should be 0 by default', () => {
      const sheet = jss.createStyleSheet({})
      expect(sheet.options.index).to.be(0)
    })

    it('should be set by the options argument', () => {
      [-50, 0, 50, 9999].forEach((n) => {
        const sheet2 = jss.createStyleSheet({}, {index: n})
        expect(sheet2.options.index).to.be(n)
      })
    })
  })

  describe('.addRule()', () => {
    let sheet
    let rule
    let style

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      rule = sheet.addRule('a', {float: 'left'})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render only 1 rule', () => {
      expect(getRules(style).length).to.be(1)
    })

    it('should render correct CSS', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })

    it('should register the rule', () => {
      expect(sheet.getRule('a')).to.be(rule)
    })

    it('should add a rule to a detached sheet', () => {
      sheet.detach()
      const newRule = sheet.addRule('b', {float: 'right'})
      sheet.attach()
      expect(sheet.getRule('b')).to.be(newRule)
    })

    it('should link sheet in rules options', () => {
      expect(sheet.getRule('a').options.sheet).to.be(sheet)
    })
  })

  describe('.addRule() with .addRule() call within a plugin and attached sheet', () => {
    let style
    let sheet

    beforeEach(() => {
      function addRule(rule) {
        if (rule.name === 'a') {
          rule.options.sheet.addRule('b', {color: 'red'})
        }
      }
      const localJss = create(settings).use(addRule)
      sheet = localJss.createStyleSheet().attach()
      sheet.addRule('a', {color: 'green'})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render rules in the right order', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with @media and attached sheet', () => {
    let style
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('a', {color: 'red'})
      // It is important to use exactly this query, because
      // IE will add "all" always when `cssRules.insertRule` is used,
      // however all others will always remove "all" if query contains a second
      // condition.
      sheet.addRule('@media all', {
        a: {color: 'green'}
      })
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render @media', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with just function values and attached sheet', () => {
    let style
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach().link()
      sheet.addRule('a', {color: ({color}) => color})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render an empty rule', () => {
      expect(getCss(style)).to.be(removeWhitespace(sheet.toString()))
    })

    it('should render rule with updated color', () => {
      sheet.update({color: 'red'})
      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: red;
        }
      `)
    })
  })

  describe('.addRule() with empty styles', () => {
    let sheet
    let style
    let warned = false

    beforeEach(() => {
      DomRenderer.__Rewire__('warning', () => {
        warned = true
      })
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('a', {})
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
      DomRenderer.__ResetDependency__('warning')
    })

    it('should not render', () => {
      expect(getCss(style)).to.be('')
    })

    it('should not warn', () => {
      expect(warned).to.be(false)
    })
  })

  describe('.addRule() with @keyframes and attached sheet', () => {
    const isSupported = 'animationName' in document.body.style
    let style
    let sheet

    // We skip this test as keyframes are not supported by browser.
    if (!isSupported) return

    beforeEach(() => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('@keyframes id', {
        '0%': {top: '0px'}
      })
      style = getStyle()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should render @keyframes', () => {
      // Safari adds the prefix automatically.
      const css = getCss(style).replace('-webkit-', '')
      expect(css).to.be(removeWhitespace(sheet.toString()))
    })
  })

  describe('.addRule() with invalid decl to attached sheet', () => {
    let warned = false
    let sheet

    beforeEach(() => {
      DomRenderer.__Rewire__('warning', () => {
        warned = true
      })
    })

    afterEach(() => {
      DomRenderer.__ResetDependency__('warning')
      sheet.detach()
    })

    it('should not throw', () => {
      sheet = jss.createStyleSheet().attach()
      sheet.addRule('%%%%', {color: 'red'})
      expect(warned).to.be(true)
    })
  })

  describe('.addRules() with an attached sheet', () => {
    let sheet
    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {width: '1px'}
      }).attach()
      sheet.addRules({
        b: {
          width: '2px'
        }
      })
    })

    it('should add rules to an attached sheet', () => {
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(computeStyle(sheet.classes.b).width).to.be('2px')
    })

    it('should keep the values after reattaching', () => {
      sheet.detach().attach()
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(computeStyle(sheet.classes.b).width).to.be('2px')
    })
  })

  describe('.deleteRule()', () => {
    it('should delete a rule from the sheet and DOM', () => {
      const sheet = jss.createStyleSheet(
        {a: {width: '1px'}},
        {link: true}
      ).attach()
      expect(computeStyle(sheet.classes.a).width).to.be('1px')
      expect(sheet.deleteRule('a')).to.be(true)
      expect(sheet.getRule('a')).to.be(undefined)
      expect(computeStyle(sheet.classes.a).width).not.to.be('1px')
      sheet.detach()
    })
  })

  describe('rule.prop()', () => {
    let rule
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({a: {color: 'green'}}, {link: true})
      rule = sheet.getRule('a')
      sheet.attach()
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should have initial color', () => {
      expect(rule.style.color).to.be('green')
    })

    it('should apply a style prop', () => {
      rule.prop('color', 'red')
      expect(rule.prop('color')).to.be('red')
    })

    it('should cache the new prop', () => {
      rule.prop('color', 'red')
      expect(rule.style.color).to.be('red')
    })

    it('should apply new prop to the DOM', () => {
      rule.prop('color', 'red')
      expect(rule.renderable.style.color).to.be('red')
    })
  })

  describe('rule.selector', () => {
    let sheet
    let rule

    beforeEach(() => {
      sheet = jss.createStyleSheet(
        {a: {width: '1px'}},
        {link: true}
      ).attach()
      rule = sheet.getRule('a')
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should set the selector', () => {
      rule.selector = '.test'
      expect(rule.selector).to.be('.test')
    })

    it('should apply selector to the DOM', () => {
      rule.selector = '.test'
      expect(computeStyle('test').width).to.be('1px')
    })

    it('should register the class name in sheet.classes', () => {
      expect(sheet.classes.a, 'test')
    })
  })

  describe('sheet.update()', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {
          color: theme => theme.color
        },
        '@media all': {
          b: {
            color: theme => theme.color
          }
        }
      }, {link: true})
    })

    afterEach(() => {
      sheet.detach()
    })

    it('should return correct .toString()', () => {
      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
        }
        @media all {
          .b-id {
          }
        }
      `)

      sheet.update({
        color: 'green'
      })

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: green;
        }
        @media all {
          .b-id {
            color: green;
          }
        }
      `)

      sheet.update({
        color: 'yellow'
      })

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: yellow;
        }
        @media all {
          .b-id {
            color: yellow;
          }
        }
      `)
    })

    it('should render sheet with updated props', () => {
      sheet.update({color: 'green'}).attach()
      expect(getCss(getStyle())).to.be(removeWhitespace(sheet.toString()))
    })

    it('should update specific rule', () => {
      sheet.update({color: 'yellow'})
      sheet.update('a', {color: 'green'})

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          color: green;
        }
        @media all {
          .b-id {
            color: yellow;
          }
        }
      `)
    })

    it('should render updated rule', () => {
      sheet.update('a', {color: 'green'}).attach()
      expect(getCss(getStyle())).to.be(removeWhitespace(sheet.toString()))
    })
  })
})
<|endoftext|>


<commit_before>/*
 Copyright 2012 - $Date $ by PeopleWare n.v.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

define(["dojo/has", "dojo/_base/lang", "require"],
  function(has, lang, require1) {

    var type2MidMidConfigName = "ppwcode-util-oddsAndEnds-type-2mid";

    function trace(msg) {
      var traceMsg = "type loader: " + msg;
      console.debug(traceMsg);
    }

    function error(msg) {
      var errMsg = "ERROR: " + msg;
      console.error(errMsg);
      throw errMsg;
    }

    function loaderError(mid) {
      return function(error){
        error("problem loading module '" + mid + "'");
        console.log(error.src, error.id);
      };
    }

    var type2MidCache = null;

    trace("initializing");
    var type2MidConfig = require1.rawConfig[type2MidMidConfigName];
    if (!type2MidConfig && has("dojo-debug-messages")) {
      // for testing, we try to read from the global variable type2MidMidConfigName
      type2MidConfig = this[type2MidMidConfigName];
    }
    if (!type2MidConfig) {
        error("could not find a MID name for the type2mid module to be used. " +
        "This should be specified in dojoConfig." + type2MidMidConfigName + ".");
    }
    trace("type2MidConfig is '" + type2MidConfig + "'");
    if (lang.isFunction(type2MidConfig)) {
      type2MidCache = type2MidConfig;
    }
    trace("initialization done")

    function requireType2Mid(loadRequire, continuation) {
      if (type2MidCache) {
        continuation(type2MidCache);
      }
      else {
        trace("loading '" + type2MidConfig + "'");
        loadRequire.on("error", loaderError(type2MidConfig));
        loadRequire([type2MidConfig], function(type2Mid) {
          trace("loaded '" + type2MidConfig + "' (" + type2Mid + ")");
          type2MidCache =  type2Mid;
          continuation(type2MidCache);
        });
      }
    }

    function convertType2Mid(type2Mid, serverTypeIdentifier) {
      var typeMid = null;
      try {
        trace("converting '" + serverTypeIdentifier + "'");
        typeMid = type2Mid(serverTypeIdentifier);
        trace("resulting MID: '" + typeMid + "'");
      }
      catch (e) {
        error("could not convert server type identifier '" + serverTypeIdentifier +
          "' to MID using " + type2MidConfig + "(" + e + ")");
      }
      return typeMid;
    }

    function requireType(loadRequire, type2Mid, serverTypeIdentifier, continuation) {
      var typeMid = convertType2Mid(type2Mid, serverTypeIdentifier);
      trace("loading '" + typeMid + "'");
      loadRequire.on("error", loaderError(typeMid));
      loadRequire([typeMid], function(type) {
        // the type is now loaded (it should be a constructor); we are ready
        trace("loaded '" + typeMid + "' (" + type + ")");
        continuation(type);
      });
    }

    var load = function(serverTypeIdentifier, // the string to the right of the !
                        loadRequire,    // AMD require; usually a context-sensitive require bound to the module making the plugin request
                        callback) { // the function the plugin should call with the return value once it is done
      trace("load requested: " + serverTypeIdentifier);
      requireType2Mid(loadRequire, function(type2Mid) {
        requireType(loadRequire, type2Mid, serverTypeIdentifier, callback);
      });
    };

    return {
      // summary:
      //    Require plugin to load modules that define types after converting an identification
      //    to a MID. This resolves issues with
      //    * syncing types defined on the server with types defined in the client
      //    * loading classes when polymorph results are received.
      // description:
      //    The main issue that is de raison d'être for this module is the observation
      //    that in a RESTful JSON API might return a more specialized type of object than
      //    requested.
      //    E.g., consider a case where there is a type `Person`, with 2 subtypes, `Male`
      //    and `Female`. When asking the server for a spouse, the resulting object is
      //    not a `Person` (which is abstract), but either a `Male` or a `Female`, and
      //    we don't know in advance which. Suppose we ask the server for relatives,
      //    we would receive a collection of `Person` instances, and again, we can't
      //    know ourselves which of these is `Male`, and which is a `Female`. The server
      //    has to tell us.
      //
      //    We make abstraction of how the server tells us. When we use, e.g., JSON.net
      //    (http://james.newtonking.com/projects/json-net.aspx) on the server, the type
      //    of the object can be included in the serialized JSON representation as a
      //    property `"$type"`. Other mechanisms might do this in another way.
      //
      //    This leads to the second goal of this plugin. The way the type is represented
      //    in a JSON object we receive from the server is not only abstract in "how", but
      //    also in "what". In the example of JSON.net, the `"$type"` property will contain
      //    the fully qualified class name, the .net way (including the name of the DLL,
      //    and optionally, the version of the DLL, etc.:
      //    `"MyProject.Foo.Bar.ANiceType, MyProject.Foo, Version=3.4.2.0, Culture=neutral, PublicKeyToken=null"`).
      //    What we actually need to load the correct module, of course, is a MID.
      //    This means we need to map these `"$type"`-values to a MID. Using a map comes to
      //    mind, but we know that this is prone to change (e.g., during refactorings).
      //    Of course, we could do the mapping in the server, but we believe it is better to
      //    let the server be authoritative, and to have the client follow.
      //
      //    How this mapping is done is abstract in this function, but we suggest that you take
      //    an approach of "convention over configuration", possibly overridden for some extreme
      //    cases with explicit mapping.
      //
      //    The server-type information should be passed to the plugin as an argument:
      //
      //    example:
      //	  | define(
      //    |   ["ppwcode/util/type!MyProject.Foo.Bar.ANiceType, MyProject.Foo, Version=3.4.2.0, Culture=neutral, PublicKeyToken=null", ...],
      //    |   function(AConstructor, ...) {
      //    |   }
      //    | );
      //
      //    Although what is loaded in a plugin is in general not automatically stored in the loader cache,
      //    in this case it is. This means that modules loaded this way can still be loaded in the regular way
      //    elsewhere too:
      //
      //    example:
      //	  | define(
      //    |   ["Bar/ANiceType", ...],
      //    |   function(TheNiceTypeConstructor, ...) {
      //    |   }
      //    | );
      //
      //    What is left is a way to specify the type-description-to-MID-mapping.
      //    It would be unwieldy to have users specify this mapping with each use of the plugin. Most of the time,
      //    there will be one overall mapping for a given project. Therefor, the mapping is specified in the
      //    configuration `dojoConfig`.
      //    Since `dojoConfig` must be specified before dojo is loaded, and we do not want to keep you from using
      //    dojo in the definition of the mapping, the configuration can contain the MID of a module that
      //    defines the application mapper, or a function.
      //    The name of the configuration parameter is `ppwcode-util-oddsAndEnds-type-2mid`.
      //
      //    example:
      //    | <script>
      //    | var dojoConfig = {
      //    |   async: true,
      //    |   parseOnLoad: false,
      //    |   ppwcode-util-oddsAndEnds-type-2mid: function(serverTypeIdentifier) {
      //    |     return serverTypeIdentifier;
      //    |   },
      //    |   has: {
      //    |     "dojo-debug-messages": true,
      //    |     "mvc-bindings-log-api": true
      //    |   }
      //    | };
      //    | </script>

      //    example:
      //    | <script>
      //    | var dojoConfig = {
      //    |   async: true,
      //    |   parseOnLoad: false,
      //    |   ppwcode-util-oddsAndEnds-type-2mid: "MyProject/UI/type2mid",
      //    |   has: {
      //    |     "dojo-debug-messages": true,
      //    |     "mvc-bindings-log-api": true
      //    |   }
      //    | };
      //    | </script>
      //
      //    The `ppwcode-util-oddsAndEnds-type-2mid-MID` module should return a function, that accepts a
      //    string type identified (as defined by the server), and returns the mapped MID.

      load: load
    };
  }
);
<commit_msg>resolved stupid name conflict<commit_after>/*
 Copyright 2012 - $Date $ by PeopleWare n.v.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

define(["dojo/has", "dojo/_base/lang", "require"],
  function(has, lang, require1) {

    var type2MidMidConfigName = "ppwcode-util-oddsAndEnds-type-2mid";

    function trace(msg) {
      var traceMsg = "type loader: " + msg;
      console.debug(traceMsg);
    }

    function error(msg) {
      var errMsg = "ERROR: " + msg;
      console.error(errMsg);
      throw errMsg;
    }

    function loaderError(mid) {
      return function(e){
        error("problem loading module '" + mid + "'");
        console.log(e.src, e.id);
      };
    }

    var type2MidCache = null;

    trace("initializing");
    var type2MidConfig = require1.rawConfig[type2MidMidConfigName];
    if (!type2MidConfig && has("dojo-debug-messages")) {
      // for testing, we try to read from the global variable type2MidMidConfigName
      type2MidConfig = this[type2MidMidConfigName];
    }
    if (!type2MidConfig) {
        error("could not find a MID name for the type2mid module to be used. " +
        "This should be specified in dojoConfig." + type2MidMidConfigName + ".");
    }
    trace("type2MidConfig is '" + type2MidConfig + "'");
    if (lang.isFunction(type2MidConfig)) {
      type2MidCache = type2MidConfig;
    }
    trace("initialization done");

    function requireType2Mid(loadRequire, continuation) {
      if (type2MidCache) {
        continuation(type2MidCache);
      }
      else {
        trace("loading '" + type2MidConfig + "'");
        loadRequire.on("error", loaderError(type2MidConfig));
        loadRequire([type2MidConfig], function(type2Mid) {
          trace("loaded '" + type2MidConfig + "' (" + type2Mid + ")");
          type2MidCache =  type2Mid;
          continuation(type2MidCache);
        });
      }
    }

    function convertType2Mid(type2Mid, serverTypeIdentifier) {
      var typeMid = null;
      try {
        trace("converting '" + serverTypeIdentifier + "'");
        typeMid = type2Mid(serverTypeIdentifier);
        trace("resulting MID: '" + typeMid + "'");
      }
      catch (e) {
        error("could not convert server type identifier '" + serverTypeIdentifier +
          "' to MID using " + type2MidConfig + "(" + e + ")");
      }
      return typeMid;
    }

    function requireType(loadRequire, type2Mid, serverTypeIdentifier, continuation) {
      var typeMid = convertType2Mid(type2Mid, serverTypeIdentifier);
      trace("loading '" + typeMid + "'");
      loadRequire.on("error", loaderError(typeMid));
      loadRequire([typeMid], function(type) {
        // the type is now loaded (it should be a constructor); we are ready
        trace("loaded '" + typeMid + "' (" + type + ")");
        continuation(type);
      });
    }

    var load = function(serverTypeIdentifier, // the string to the right of the !
                        loadRequire,    // AMD require; usually a context-sensitive require bound to the module making the plugin request
                        callback) { // the function the plugin should call with the return value once it is done
      trace("load requested: " + serverTypeIdentifier);
      requireType2Mid(loadRequire, function(type2Mid) {
        requireType(loadRequire, type2Mid, serverTypeIdentifier, callback);
      });
    };

    return {
      // summary:
      //    Require plugin to load modules that define types after converting an identification
      //    to a MID. This resolves issues with
      //    * syncing types defined on the server with types defined in the client
      //    * loading classes when polymorph results are received.
      // description:
      //    The main issue that is de raison d'être for this module is the observation
      //    that in a RESTful JSON API might return a more specialized type of object than
      //    requested.
      //    E.g., consider a case where there is a type `Person`, with 2 subtypes, `Male`
      //    and `Female`. When asking the server for a spouse, the resulting object is
      //    not a `Person` (which is abstract), but either a `Male` or a `Female`, and
      //    we don't know in advance which. Suppose we ask the server for relatives,
      //    we would receive a collection of `Person` instances, and again, we can't
      //    know ourselves which of these is `Male`, and which is a `Female`. The server
      //    has to tell us.
      //
      //    We make abstraction of how the server tells us. When we use, e.g., JSON.net
      //    (http://james.newtonking.com/projects/json-net.aspx) on the server, the type
      //    of the object can be included in the serialized JSON representation as a
      //    property `"$type"`. Other mechanisms might do this in another way.
      //
      //    This leads to the second goal of this plugin. The way the type is represented
      //    in a JSON object we receive from the server is not only abstract in "how", but
      //    also in "what". In the example of JSON.net, the `"$type"` property will contain
      //    the fully qualified class name, the .net way (including the name of the DLL,
      //    and optionally, the version of the DLL, etc.:
      //    `"MyProject.Foo.Bar.ANiceType, MyProject.Foo, Version=3.4.2.0, Culture=neutral, PublicKeyToken=null"`).
      //    What we actually need to load the correct module, of course, is a MID.
      //    This means we need to map these `"$type"`-values to a MID. Using a map comes to
      //    mind, but we know that this is prone to change (e.g., during refactorings).
      //    Of course, we could do the mapping in the server, but we believe it is better to
      //    let the server be authoritative, and to have the client follow.
      //
      //    How this mapping is done is abstract in this function, but we suggest that you take
      //    an approach of "convention over configuration", possibly overridden for some extreme
      //    cases with explicit mapping.
      //
      //    The server-type information should be passed to the plugin as an argument:
      //
      //    example:
      //	  | define(
      //    |   ["ppwcode/util/type!MyProject.Foo.Bar.ANiceType, MyProject.Foo, Version=3.4.2.0, Culture=neutral, PublicKeyToken=null", ...],
      //    |   function(AConstructor, ...) {
      //    |   }
      //    | );
      //
      //    Although what is loaded in a plugin is in general not automatically stored in the loader cache,
      //    in this case it is. This means that modules loaded this way can still be loaded in the regular way
      //    elsewhere too:
      //
      //    example:
      //	  | define(
      //    |   ["Bar/ANiceType", ...],
      //    |   function(TheNiceTypeConstructor, ...) {
      //    |   }
      //    | );
      //
      //    What is left is a way to specify the type-description-to-MID-mapping.
      //    It would be unwieldy to have users specify this mapping with each use of the plugin. Most of the time,
      //    there will be one overall mapping for a given project. Therefor, the mapping is specified in the
      //    configuration `dojoConfig`.
      //    Since `dojoConfig` must be specified before dojo is loaded, and we do not want to keep you from using
      //    dojo in the definition of the mapping, the configuration can contain the MID of a module that
      //    defines the application mapper, or a function.
      //    The name of the configuration parameter is `ppwcode-util-oddsAndEnds-type-2mid`.
      //
      //    example:
      //    | <script>
      //    | var dojoConfig = {
      //    |   async: true,
      //    |   parseOnLoad: false,
      //    |   ppwcode-util-oddsAndEnds-type-2mid: function(serverTypeIdentifier) {
      //    |     return serverTypeIdentifier;
      //    |   },
      //    |   has: {
      //    |     "dojo-debug-messages": true,
      //    |     "mvc-bindings-log-api": true
      //    |   }
      //    | };
      //    | </script>

      //    example:
      //    | <script>
      //    | var dojoConfig = {
      //    |   async: true,
      //    |   parseOnLoad: false,
      //    |   ppwcode-util-oddsAndEnds-type-2mid: "MyProject/UI/type2mid",
      //    |   has: {
      //    |     "dojo-debug-messages": true,
      //    |     "mvc-bindings-log-api": true
      //    |   }
      //    | };
      //    | </script>
      //
      //    The `ppwcode-util-oddsAndEnds-type-2mid-MID` module should return a function, that accepts a
      //    string type identified (as defined by the server), and returns the mapped MID.

      load: load
    };
  }
);
<|endoftext|>


<commit_before>// FIXME add GeometryCollection

goog.provide('ol.geom.Geometry');

goog.require('goog.asserts');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('ol.extent');
goog.require('ol.geom.flat');

/**
 * @enum {string}
 */
ol.geom.Type = {
  POINT: 'Point',
  LINE_STRING: 'LineString',
  LINEAR_RING: 'LinearRing',
  POLYGON: 'Polygon',
  MULTI_POINT: 'MultiPoint',
  MULTI_LINE_STRING: 'MultiLineString',
  MULTI_POLYGON: 'MultiPolygon'
};

/**
 * @enum {string}
 */
ol.geom.Layout = {
  XY: 'XY',
  XYZ: 'XYZ',
  XYM: 'XYM',
  XYZM: 'XYZM'
};

/**
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.geom.Geometry = function() {

  goog.base(this);

  /**
   * @protected
   * @type {ol.geom.Layout}
   */
  this.layout = ol.geom.Layout.XY;

  /**
   * @protected
   * @type {number}
   */
  this.stride = 2;

  /**
   * @protected
   * @type {Array.<number>}
   */
  this.flatCoordinates = [];

  /**
   * @protected
   * @type {number}
   */
  this.revision = 0;

  /**
   * @protected
   * @type {ol.Extent|undefined}
   */
  this.extent = undefined;

  /**
   * @protected
   * @type {number}
   */
  this.extentRevision = -1;

};
goog.inherits(ol.geom.Geometry, goog.events.EventTarget);

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @return {boolean} Contains coordinate.
 */
ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) {
  return this.containsXY(coordinate[0], coordinate[1]);
};

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @return {boolean} Contains (x, y).
 */
ol.geom.Geometry.prototype.containsXY = goog.functions.FALSE;

/**
 * FIXME empty description for jsdoc
 */
ol.geom.Geometry.prototype.dispatchChangeEvent = function() {
  ++this.revision;
  this.dispatchEvent(goog.events.EventType.CHANGE);
};

/**
 * @param {ol.Extent=} opt_extent Extent.
 * @return {ol.Extent} extent Extent.
 */
ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
  if (this.extentRevision != this.revision) {
    this.extent = ol.extent.createOrUpdateFromFlatCoordinates(
        this.flatCoordinates, this.stride, this.extent);
    this.extentRevision = this.revision;
  }
  goog.asserts.assert(goog.isDef(this.extent));
  return ol.extent.returnOrUpdate(this.extent, opt_extent);
};

/**
 * @return {Array.<number>} Flat coordinates.
 */
ol.geom.Geometry.prototype.getFlatCoordinates = function() {
  return this.flatCoordinates;
};

/**
 * @return {ol.geom.Layout} Layout.
 */
ol.geom.Geometry.prototype.getLayout = function() {
  return this.layout;
};

/**
 * @return {number} Revision.
 */
ol.geom.Geometry.prototype.getRevision = function() {
  return this.revision;
};

/**
 * @return {number} Stride.
 */
ol.geom.Geometry.prototype.getStride = function() {
  return this.stride;
};

/**
 * @return {ol.geom.Type} Geometry type.
 */
ol.geom.Geometry.prototype.getType = goog.abstractMethod;

/**
 * @param {ol.geom.Layout|undefined} layout Layout.
 * @param {Array} coordinates Coordinates.
 * @param {number} nesting Nesting.
 * @protected
 */
ol.geom.Geometry.prototype.setLayout =
    function(layout, coordinates, nesting) {
  /** @type {number} */
  var stride;
  if (goog.isDef(layout)) {
    if (layout == ol.geom.Layout.XY) {
      stride = 2;
    } else if (layout == ol.geom.Layout.XYZ) {
      stride = 3;
    } else if (layout == ol.geom.Layout.XYM) {
      stride = 3;
    } else if (layout == ol.geom.Layout.XYZM) {
      stride = 4;
    } else {
      throw new Error('unsupported layout: ' + layout);
    }
  } else {
    var i;
    for (i = 0; i < nesting; ++i) {
      if (coordinates.length === 0) {
        this.layout = ol.geom.Layout.XY;
        this.stride = 2;
        return;
      } else {
        coordinates = /** @type {Array} */ (coordinates[0]);
      }
    }
    stride = (/** @type {Array} */ (coordinates)).length;
    if (stride == 2) {
      layout = ol.geom.Layout.XY;
    } else if (stride == 3) {
      layout = ol.geom.Layout.XYZ;
    } else if (stride == 4) {
      layout = ol.geom.Layout.XYZM;
    } else {
      throw new Error('unsupported stride: ' + stride);
    }
  }
  this.layout = layout;
  this.stride = stride;
};

/**
 * @param {ol.TransformFunction} transformFn Transform.
 */
ol.geom.Geometry.prototype.transform = function(transformFn) {
  transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
};

/**
 * @typedef {ol.Coordinate}
 */
ol.geom.RawPoint;

/**
 * @typedef {Array.<ol.Coordinate>}
 */
ol.geom.RawLineString;

/**
 * @typedef {Array.<ol.Coordinate>}
 *
 */
ol.geom.RawLinearRing;

/**
 * @typedef {Array.<ol.geom.RawLinearRing>}
 */
ol.geom.RawPolygon;

/**
 * @typedef {Array.<ol.geom.RawPoint>}
 */
ol.geom.RawMultiPoint;

/**
 * @typedef {Array.<ol.geom.RawLineString>}
 */
ol.geom.RawMultiLineString;

/**
 * @typedef {Array.<ol.geom.RawPolygon>}
 */
ol.geom.RawMultiPolygon;

/**
 * @param {ol.geom.Geometry} geometry Geometry.
 * @param {goog.vec.Mat4.AnyType} transform Transform.
 * @param {Array.<number>=} opt_dest Destination.
 * @return {Array.<number>} Transformed flat coordinates.
 */
ol.geom.transformGeometry2D = function(geometry, transform, opt_dest) {
  var flatCoordinates = geometry.getFlatCoordinates();
  var stride = geometry.getStride();
  return ol.geom.flat.transform2D(
      flatCoordinates, stride, transform, opt_dest);
};
<commit_msg>Factor out ol.geom.Geometry.getLayoutForStride_<commit_after>// FIXME add GeometryCollection

goog.provide('ol.geom.Geometry');

goog.require('goog.asserts');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('ol.extent');
goog.require('ol.geom.flat');

/**
 * @enum {string}
 */
ol.geom.Type = {
  POINT: 'Point',
  LINE_STRING: 'LineString',
  LINEAR_RING: 'LinearRing',
  POLYGON: 'Polygon',
  MULTI_POINT: 'MultiPoint',
  MULTI_LINE_STRING: 'MultiLineString',
  MULTI_POLYGON: 'MultiPolygon'
};

/**
 * @enum {string}
 */
ol.geom.Layout = {
  XY: 'XY',
  XYZ: 'XYZ',
  XYM: 'XYM',
  XYZM: 'XYZM'
};

/**
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.geom.Geometry = function() {

  goog.base(this);

  /**
   * @protected
   * @type {ol.geom.Layout}
   */
  this.layout = ol.geom.Layout.XY;

  /**
   * @protected
   * @type {number}
   */
  this.stride = 2;

  /**
   * @protected
   * @type {Array.<number>}
   */
  this.flatCoordinates = [];

  /**
   * @protected
   * @type {number}
   */
  this.revision = 0;

  /**
   * @protected
   * @type {ol.Extent|undefined}
   */
  this.extent = undefined;

  /**
   * @protected
   * @type {number}
   */
  this.extentRevision = -1;

};
goog.inherits(ol.geom.Geometry, goog.events.EventTarget);

/**
 * @param {number} stride Stride.
 * @private
 * @return {ol.geom.Layout} layout Layout.
 */
ol.geom.Geometry.getLayoutForStride_ = function(stride) {
  if (stride == 2) {
    return ol.geom.Layout.XY;
  } else if (stride == 3) {
    return ol.geom.Layout.XYZ;
  } else if (stride == 4) {
    return ol.geom.Layout.XYZM;
  } else {
    throw new Error('unsupported stride: ' + stride);
  }
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @return {boolean} Contains coordinate.
 */
ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) {
  return this.containsXY(coordinate[0], coordinate[1]);
};

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @return {boolean} Contains (x, y).
 */
ol.geom.Geometry.prototype.containsXY = goog.functions.FALSE;

/**
 * FIXME empty description for jsdoc
 */
ol.geom.Geometry.prototype.dispatchChangeEvent = function() {
  ++this.revision;
  this.dispatchEvent(goog.events.EventType.CHANGE);
};

/**
 * @param {ol.Extent=} opt_extent Extent.
 * @return {ol.Extent} extent Extent.
 */
ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
  if (this.extentRevision != this.revision) {
    this.extent = ol.extent.createOrUpdateFromFlatCoordinates(
        this.flatCoordinates, this.stride, this.extent);
    this.extentRevision = this.revision;
  }
  goog.asserts.assert(goog.isDef(this.extent));
  return ol.extent.returnOrUpdate(this.extent, opt_extent);
};

/**
 * @return {Array.<number>} Flat coordinates.
 */
ol.geom.Geometry.prototype.getFlatCoordinates = function() {
  return this.flatCoordinates;
};

/**
 * @return {ol.geom.Layout} Layout.
 */
ol.geom.Geometry.prototype.getLayout = function() {
  return this.layout;
};

/**
 * @return {number} Revision.
 */
ol.geom.Geometry.prototype.getRevision = function() {
  return this.revision;
};

/**
 * @return {number} Stride.
 */
ol.geom.Geometry.prototype.getStride = function() {
  return this.stride;
};

/**
 * @return {ol.geom.Type} Geometry type.
 */
ol.geom.Geometry.prototype.getType = goog.abstractMethod;

/**
 * @param {ol.geom.Layout|undefined} layout Layout.
 * @param {Array} coordinates Coordinates.
 * @param {number} nesting Nesting.
 * @protected
 */
ol.geom.Geometry.prototype.setLayout =
    function(layout, coordinates, nesting) {
  /** @type {number} */
  var stride;
  if (goog.isDef(layout)) {
    if (layout == ol.geom.Layout.XY) {
      stride = 2;
    } else if (layout == ol.geom.Layout.XYZ) {
      stride = 3;
    } else if (layout == ol.geom.Layout.XYM) {
      stride = 3;
    } else if (layout == ol.geom.Layout.XYZM) {
      stride = 4;
    } else {
      throw new Error('unsupported layout: ' + layout);
    }
  } else {
    var i;
    for (i = 0; i < nesting; ++i) {
      if (coordinates.length === 0) {
        this.layout = ol.geom.Layout.XY;
        this.stride = 2;
        return;
      } else {
        coordinates = /** @type {Array} */ (coordinates[0]);
      }
    }
    stride = (/** @type {Array} */ (coordinates)).length;
    layout = ol.geom.Geometry.getLayoutForStride_(stride);
  }
  this.layout = layout;
  this.stride = stride;
};

/**
 * @param {ol.TransformFunction} transformFn Transform.
 */
ol.geom.Geometry.prototype.transform = function(transformFn) {
  transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
};

/**
 * @typedef {ol.Coordinate}
 */
ol.geom.RawPoint;

/**
 * @typedef {Array.<ol.Coordinate>}
 */
ol.geom.RawLineString;

/**
 * @typedef {Array.<ol.Coordinate>}
 *
 */
ol.geom.RawLinearRing;

/**
 * @typedef {Array.<ol.geom.RawLinearRing>}
 */
ol.geom.RawPolygon;

/**
 * @typedef {Array.<ol.geom.RawPoint>}
 */
ol.geom.RawMultiPoint;

/**
 * @typedef {Array.<ol.geom.RawLineString>}
 */
ol.geom.RawMultiLineString;

/**
 * @typedef {Array.<ol.geom.RawPolygon>}
 */
ol.geom.RawMultiPolygon;

/**
 * @param {ol.geom.Geometry} geometry Geometry.
 * @param {goog.vec.Mat4.AnyType} transform Transform.
 * @param {Array.<number>=} opt_dest Destination.
 * @return {Array.<number>} Transformed flat coordinates.
 */
ol.geom.transformGeometry2D = function(geometry, transform, opt_dest) {
  var flatCoordinates = geometry.getFlatCoordinates();
  var stride = geometry.getStride();
  return ol.geom.flat.transform2D(
      flatCoordinates, stride, transform, opt_dest);
};
<|endoftext|>


<commit_before>const cachios = require('./../src');

const express = require('express');
const axios = require('axios');

const HITS_TO_PERFORM = 100;
const HOST = '127.0.0.1';
const PORT = 11888;
const BASE = `http://${HOST}:${PORT}`;

describe('cachios - integration', () => {
  let server;
  let socket;

  beforeEach((done) => {
    server = express();
    server.all('*', (req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'X-Requested-With');
      next();
    });
    socket = server.listen(PORT, done);
  });

  afterEach((done) => {
    if (!server || !socket) {
      return done();
    }

    socket.close(done);
    server = undefined;
    socket = undefined;
  });

  const methods = [
    'get',
    'post',
    'put',
    'patch',
    'delete',
  ];

  methods.forEach((method) => {
    test(`should work in a somewhat-real environment: ${method}`, () => {
      let hits = 0;

      server[method]('/foo', (req, res) => {
        res.type('text');
        res.send('foobar');
        hits += 1;
      });

      const hitAndCheck = (expectedHits) => Promise.resolve()
        .then(() => cachios[method](`${BASE}/foo`))
        .then((resp) => {
          expect(resp.data).toBe('foobar');
          expect(hits).toBe(expectedHits);
        });

      let promise = Promise.resolve();

      for (let i = 0; i < HITS_TO_PERFORM; i += 1) {
        promise = promise.then(() => hitAndCheck(1));
      }

      // clear cache and hit again, expecting our hits counter to increment.
      // (ensures hit counter at least works a little)
      promise = promise
      .then(() => cachios.cache.flushAll())
      .then(() => hitAndCheck(2));

      return promise;
    });
  });
});
<commit_msg>test: extract hitCounter and hitAndCheck logic from internal test method to general case<commit_after>const cachios = require('./../src');

const express = require('express');
const axios = require('axios');

const HITS_TO_PERFORM = 100;
const HOST = '127.0.0.1';
const PORT = 11888;
const BASE = `http://${HOST}:${PORT}`;

describe('cachios - integration', () => {
  let server;
  let socket;

  beforeEach((done) => {
    server = express();
    server.all('*', (req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'X-Requested-With');
      next();
    });
    socket = server.listen(PORT, done);
  });

  afterEach((done) => {
    if (!server || !socket) {
      return done();
    }

    socket.close(done);
    server = undefined;
    socket = undefined;
  });

  const methods = [
    'get',
    'post',
    'put',
    'patch',
    'delete',
  ];

  const makeHitCounter = (instance, method, base) => {
    const hitCounter = {
      hits: 0,
      method,
      endpoint: '/foo',
      body: 'foobar',
    };

    hitCounter.url = () => `${base}${hitCounter.endpoint}`;

    instance[method](hitCounter.endpoint, (req, res) => {
      res.type('text');
      res.send(hitCounter.body);
      hitCounter.hits += 1;
    });

    return hitCounter;
  };

  // hit our test url and check if we hit it or if it was returned from cache
  const hitAndCheck = (hitCounter, cachiosInstance, expectedHits) => {
    const url = hitCounter.url();
    return Promise.resolve()
      .then(() => cachiosInstance[hitCounter.method](url))
      .then((resp) => {
        // make sure we receive the expected text
        expect(resp.data).toBe(hitCounter.body);
        // make sure our hit counter matches up with our expected amount of hits
        expect(hitCounter.hits).toBe(expectedHits);
      });
  };

  methods.forEach((method) => {
    test(`should work in a somewhat-real environment: ${method}`, () => {
      const hitCounter = makeHitCounter(server, method, BASE);

      let promise = Promise.resolve();

      for (let i = 0; i < HITS_TO_PERFORM; i += 1) {
        promise = promise.then(() => hitAndCheck(hitCounter, cachios, 1));
      }

      // clear cache and hit again, expecting our hits counter to increment.
      // (ensures hit counter at least works a little)
      promise = promise
      .then(() => cachios.cache.flushAll())
      .then(() => hitAndCheck(hitCounter, cachios, 2));

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


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

/* eslint-env es6, node */

'use strict';

const Realm = require('realm');
const TestCase = require('./asserts');

function uuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function promisifiedRegister(server, username, password) {
    return new Promise((resolve, reject) => {
        Realm.Sync.User.register(server, username, password, (error, user) => {
            if (error) {
                reject(error);
            } else {
                resolve(user);
            }
        });
    });
}

function wait(delay) {
    return new Promise((resolve, reject) => setTimeout(resolve, delay));
}

module.exports = {
    testLocalRealmHasNoSession() {
        let realm = new Realm();
        TestCase.assertNull(realm.syncSession);
    },

    testProperties() {
        return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
            let config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
            let realm = new Realm(config);
            let session = realm.syncSession;

            TestCase.assertInstanceOf(session, Realm.Sync.Session);
            TestCase.assertEqual(session.user.identity, user.identity);
            TestCase.assertEqual(session.config.url, config.sync.url);
            TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
            TestCase.assertUndefined(session.url);
            TestCase.assertEqual(session.state, 'active');

            // give the session enough time to refresh its access token and bind itself
            // TODO: Use an event to discover when the session is bound
            let timeout = 3000;

            return wait(timeout).then(() => {
                TestCase.assertEqual(session.url, `realm://localhost:9080/${user.identity}/myrealm`);
            });
        });
    },

    testErrorHandling() {
        return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
            let errors = [];
            let config = { sync: { user,
                url: 'realm://localhost:9080/~/myrealm',
                error: (sender, error) => errors.push([sender, error])
            } };
            let realm = new Realm(config);
            let session = realm.syncSession;

            TestCase.assertEqual(session.config.error, config.sync.error);
            session._simulateError(123, 'simulated error');

            return wait(100).then(() => {
                TestCase.assertArrayLength(errors, 1);
                TestCase.assertEqual(errors[0][0].config.url, session.config.url);
                TestCase.assertEqual(errors[0][1].message, 'simulated error');
                TestCase.assertEqual(errors[0][1].code, 123);
            });
        });
    }
}<commit_msg>Increase timeouts to avoid spurious failures<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

/* eslint-env es6, node */

'use strict';

const Realm = require('realm');
const TestCase = require('./asserts');

function uuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function promisifiedRegister(server, username, password) {
    return new Promise((resolve, reject) => {
        Realm.Sync.User.register(server, username, password, (error, user) => {
            if (error) {
                reject(error);
            } else {
                resolve(user);
            }
        });
    });
}

function wait(delay) {
    return new Promise((resolve, reject) => setTimeout(resolve, delay));
}

module.exports = {
    testLocalRealmHasNoSession() {
        let realm = new Realm();
        TestCase.assertNull(realm.syncSession);
    },

    testProperties() {
        return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
            let config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
            let realm = new Realm(config);
            let session = realm.syncSession;

            TestCase.assertInstanceOf(session, Realm.Sync.Session);
            TestCase.assertEqual(session.user.identity, user.identity);
            TestCase.assertEqual(session.config.url, config.sync.url);
            TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
            TestCase.assertUndefined(session.url);
            TestCase.assertEqual(session.state, 'active');

            // give the session enough time to refresh its access token and bind itself
            // TODO: Use an event to discover when the session is bound
            let timeout = 4000;

            return wait(timeout).then(() => {
                TestCase.assertEqual(session.url, `realm://localhost:9080/${user.identity}/myrealm`);
            });
        });
    },

    testErrorHandling() {
        return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
            let errors = [];
            let config = { sync: { user,
                url: 'realm://localhost:9080/~/myrealm',
                error: (sender, error) => errors.push([sender, error])
            } };
            let realm = new Realm(config);
            let session = realm.syncSession;

            TestCase.assertEqual(session.config.error, config.sync.error);
            session._simulateError(123, 'simulated error');

            return wait(1000).then(() => {
                TestCase.assertArrayLength(errors, 1);
                TestCase.assertEqual(errors[0][0].config.url, session.config.url);
                TestCase.assertEqual(errors[0][1].message, 'simulated error');
                TestCase.assertEqual(errors[0][1].code, 123);
            });
        });
    }
}<|endoftext|>


<commit_before>import Controller from '@ember/controller';
import Opacity from 'ember-animated/motions/opacity';

function * transition() {
  this.insertedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { from: 0, to: 1 }));
  });

  this.receivedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { to: 1 }));
  });

  this.removedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { to: 0 }));
  });
}

function * share() {
  this.sentSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { to: 0, duration: 0 }));
  });
}

export default Controller.extend({
  transition,
  share
});
<commit_msg>remove unused code<commit_after>import Controller from '@ember/controller';
import Opacity from 'ember-animated/motions/opacity';

function * transition() {
  this.insertedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { from: 0, to: 1 }));
  });

  this.receivedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { to: 1 }));
  });

  this.removedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite, { to: 0 }));
  });
}

function * share() {
  // TODO: if we don't set a transition, our sprites aren't available
  // for far matching
}

export default Controller.extend({
  transition,
  share
});
<|endoftext|>


<commit_before>import Route from '@ember/routing/route';
import InfinityModel from 'ember-infinity/lib/infinity-model';
import { get } from '@ember/object';
import { inject as service } from '@ember/service';

const ExtendedInfinityModel =  InfinityModel.extend({
  buildParams() {
    let params = this._super(...arguments);
    params['categoryId'] = get(this, 'global').categoryId;
    return params;
  }
});

export default Route.extend({
  global: service(),
  infinity: service(),

  model() {
    let global = get(this, 'global');
    return get(this, 'infinity').model(
      'post',
      {},
      ExtendedInfinityModel.extend({ global })
    )
  }
});
<commit_msg>add "no failure case" to dummy route<commit_after>import Route from '@ember/routing/route';
import InfinityModel from 'ember-infinity/lib/infinity-model';
import { get } from '@ember/object';
import { inject as service } from '@ember/service';

const ExtendedInfinityModel =  InfinityModel.extend({
  buildParams() {
    let params = this._super(...arguments);
    params['categoryId'] = get(this, 'global').categoryId;
    return params;
  },
  afterInfinityModel(newObjects/*, infinityModel*/) {
    // smoke test.  not really doing anything.  tested at unit level
    return newObjects;
  }
});

export default Route.extend({
  global: service(),
  infinity: service(),

  model() {
    let global = get(this, 'global');
    return get(this, 'infinity').model(
      'post',
      {},
      ExtendedInfinityModel.extend({ global })
    )
  }
});
<|endoftext|>


<commit_before>// @flow
'use strict';

import React from 'react';
import muiThemeable from 'material-ui/styles/muiThemeable';
import FaqItem from './FaqItem';

class Faq extends React.Component {
    componentDidMount() : void {
        window.scrollTo(0, 0);
    }

    render() : any {
        return (

                <FaqItem
                    question="Are we going to rent a yacht and fishing at sea?"
                    answer={
Not exactly. We will meet at Tseng Tau Tsuen (井頭村), walk a few minutes to Kei Ling Ha Hoi Pier (企嶺下海碼頭), aka Sam Pui Chau Pier (三杯酒碼頭), and take a boat to Yung Shue O rafts (榕樹澳漁排), which is a group of fish cultivation facilities floating on the sea.
} />
                <FaqItem
                    question="What if there is bad weather?"
                    answer={

                                Typhoon

                                The activity will be re-scheduled when Typhoon Signal No. 8 or above is hoisted. Please check our Slack channel for announcements.

                                Rainstorm

                                If an Amber, Red, or Black Rainstorm Warning Signal is in force before the activity, we may decide to re-schedule the activity. Please check our Slack channel for announcements.

                                If an Amber, Red, or Black Rainstorm Warning Signal is in force during the activity, the activity will continue.

                    } />
                <FaqItem
                    question="Can I swim there?"
                    answer="Sure! The water quality there is good. Swimming is one of the typical activities there. Remember to bring your swimwear." />
                <FaqItem
                    question={'What is Star beach? What\'s so special about it?'}
                    answer="Blah blah blah" />
                <FaqItem
                    question="Is there any mobile network coverage?"
                    answer="Of course! It is covered by all mobile network providers." />

        );
    }
}

export default muiThemeable()(Faq);
<commit_msg>Add more items in FAQ page<commit_after>// @flow
'use strict';

import React from 'react';
import muiThemeable from 'material-ui/styles/muiThemeable';
import FaqItem from './FaqItem';

class Faq extends React.Component {
    componentDidMount() : void {
        window.scrollTo(0, 0);
    }

    render() : any {
        return (

                <FaqItem
                    question="Are we going to rent a yacht and fishing at sea?"
                    answer={
Not exactly. We will meet at Tseng Tau Tsuen (井頭村), walk a few minutes to Kei Ling Ha Hoi Pier (企嶺下海碼頭), aka Sam Pui Chau Pier (三杯酒碼頭), and take a boat to Yung Shue O rafts (榕樹澳漁排), which is a group of fish cultivation facilities floating on the sea.
} />
                <FaqItem
                    question="What if there is bad weather?"
                    answer={

                                Typhoon

                                The activity will be re-scheduled when Typhoon Signal No. 8 or above is hoisted. Please check our Slack channel for announcements.

                                Rainstorm

                                If an Amber, Red, or Black Rainstorm Warning Signal is in force before the activity, we may decide to re-schedule the activity. Please check our Slack channel for announcements.

                                If an Amber, Red, or Black Rainstorm Warning Signal is in force during the activity, the activity will continue.

                    } />
                <FaqItem
                    question="Can I swim there?"
                    answer="Sure! The water quality there is good. Swimming is one of the typical activities there. Remember to bring your swimwear." />
                <FaqItem
                    question={'What is Star beach? What\'s so special about it?'}
                    answer="Blah blah blah" />
                <FaqItem
                    question="What types of fish can I catch there?"
                    answer="Usually in summer, you will find groups of baby snappers (鱲魚) and some mottled spinefoot (泥鯭). Sometimes if you are lucky, you may catch small groupers (石斑)." />
                <FaqItem
                    question="Is there any mobile network coverage?"
                    answer="Of course! It is covered by all mobile network providers." />

        );
    }
}

export default muiThemeable()(Faq);
<|endoftext|>


<commit_before>define([
    "jquery",
    "../registry",
    "../core/logging",
    "../lib/dependshandler",
    "../core/parser"
], function($, patterns, logging, DependsHander, Parser) {
    var log = logging.getLogger("depends"),
        parser = new Parser("depends");

    parser.add_argument("condition");
    parser.add_argument("action", "show", ["show", "enable"]);
    parser.add_argument("transition", "none", ["none", "css", "fade", "slide"]);
    parser.add_argument("effect-duration", "fast");

    var depends = {
        name: "depends",
        trigger: ".pat-depends",
        jquery_plugin: true,

        transitions: {
            none: {hide: "hide", show: "show"},
            fade: {hide: "fadeOut", show: "fadeIn"},
            slide: {hide: "slideUp", show: "slideDown"}
        },

        init: function($el, opts) {
            return $el.each(function() {
                var slave = this,
                    $slave = $(this),
                    options = parser.parse($slave, opts),
                    handler, state;

                try {
                    handler=new DependsHander($slave, options.condition);
                } catch (e) {
                    log.error("Invalid condition: " + e.message);
                    return;
                }

                state=handler.evaluate();
                switch (options.action) {
                    case "show":
                        if (state)
                            $slave.show();
                        else
                            $slave.hide();
                        break;
                    case "enable":
                        if (state)
                            depends._enable($slave);
                        else
                            depends._disable($slave);
                        break;
                }

                var data = {handler: handler,
                            options: options,
                            slave: slave};

                handler.getAllInputs().each(function() {
                    if (this.form) {
                        var $form = $(this.form),
                            slaves = $form.data("patDepends.slaves");
                        if (!slaves) {
                            slaves=[data];
                            $form.on("reset.pat-depends", depends.onReset);
                        } else if (slaves.indexOf(data)===-1)
                            slaves.push(data);
                        $form.data("patDepends.slaves", slaves);
                    }
                    $(this).on("change.pat-depends", null, data, depends.onChange);
                });
            });
        },

        onReset: function(event) {
            var slaves = $(this).data("patDepends.slaves"),
                i;

            setTimeout(function() {
                for (i=0; i<slaves.length; i++) {
                    event.data=slaves[i];
                    depends.onChange(event);
                }
            }, 50);
        },

        _enable: function($slave) {
            if ($slave.is(":input"))
                $slave[0].disabled=null;
            else if ($slave.is("a"))
                $slave.off("click.patternDepends");
            $slave.removeClass("disabled");
        },

        _disable: function($slave) {
            if ($slave.is(":input"))
                $slave[0].disabled="disabled";
            else if ($slave.is("a"))
                $slave.on("click.patternDepends", depends.blockDefault);
            $slave.addClass("disabled");
        },

        onChange: function(event) {
            var handler = event.data.handler,
                options = event.data.options,
                slave = event.data.slave,
                $slave = $(slave),
                state = handler.evaluate();

            switch (options.action) {
                case "show":
                    $slave.removeClass("visible hidden in-progress");
                    if (options.transition==="css")
                        $slave.addClass(state ? "visible" : "hidden");
                    else {
                        $slave.addClass("in-progress");
                        var t = depends.transitions[options.transition],
                            duration = (options.transition==="none" ? null : options.effectDuration);
                        $slave[state ? t.show : t.hide](duration, function() {
                            $slave.removeClass("hidden").addClass(state ? "visible" : "hidden");
                        });
                    }
                    break;
                case "enable":
                    if (state)
                        depends._enable($slave);
                    else
                        depends._disable($slave);
                    break;
            }
        },

        blockDefault: function(event) {
            event.preventDefault();
        }
    };

    patterns.register(depends);
    return depends; // XXX for tests only
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>Depends: fix stray in-progress class<commit_after>define([
    "jquery",
    "../registry",
    "../core/logging",
    "../lib/dependshandler",
    "../core/parser"
], function($, patterns, logging, DependsHander, Parser) {
    var log = logging.getLogger("depends"),
        parser = new Parser("depends");

    parser.add_argument("condition");
    parser.add_argument("action", "show", ["show", "enable"]);
    parser.add_argument("transition", "none", ["none", "css", "fade", "slide"]);
    parser.add_argument("effect-duration", "fast");

    var depends = {
        name: "depends",
        trigger: ".pat-depends",
        jquery_plugin: true,

        transitions: {
            none: {hide: "hide", show: "show"},
            fade: {hide: "fadeOut", show: "fadeIn"},
            slide: {hide: "slideUp", show: "slideDown"}
        },

        init: function($el, opts) {
            return $el.each(function() {
                var slave = this,
                    $slave = $(this),
                    options = parser.parse($slave, opts),
                    handler, state;

                try {
                    handler=new DependsHander($slave, options.condition);
                } catch (e) {
                    log.error("Invalid condition: " + e.message);
                    return;
                }

                state=handler.evaluate();
                switch (options.action) {
                    case "show":
                        if (state)
                            $slave.show();
                        else
                            $slave.hide();
                        break;
                    case "enable":
                        if (state)
                            depends._enable($slave);
                        else
                            depends._disable($slave);
                        break;
                }

                var data = {handler: handler,
                            options: options,
                            slave: slave};

                handler.getAllInputs().each(function() {
                    if (this.form) {
                        var $form = $(this.form),
                            slaves = $form.data("patDepends.slaves");
                        if (!slaves) {
                            slaves=[data];
                            $form.on("reset.pat-depends", depends.onReset);
                        } else if (slaves.indexOf(data)===-1)
                            slaves.push(data);
                        $form.data("patDepends.slaves", slaves);
                    }
                    $(this).on("change.pat-depends", null, data, depends.onChange);
                });
            });
        },

        onReset: function(event) {
            var slaves = $(this).data("patDepends.slaves"),
                i;

            setTimeout(function() {
                for (i=0; i<slaves.length; i++) {
                    event.data=slaves[i];
                    depends.onChange(event);
                }
            }, 50);
        },

        _enable: function($slave) {
            if ($slave.is(":input"))
                $slave[0].disabled=null;
            else if ($slave.is("a"))
                $slave.off("click.patternDepends");
            $slave.removeClass("disabled");
        },

        _disable: function($slave) {
            if ($slave.is(":input"))
                $slave[0].disabled="disabled";
            else if ($slave.is("a"))
                $slave.on("click.patternDepends", depends.blockDefault);
            $slave.addClass("disabled");
        },

        onChange: function(event) {
            var handler = event.data.handler,
                options = event.data.options,
                slave = event.data.slave,
                $slave = $(slave),
                state = handler.evaluate();

            switch (options.action) {
                case "show":
                    $slave.removeClass("visible hidden in-progress");
                    if (options.transition==="css")
                        $slave.addClass(state ? "visible" : "hidden");
                    else {
                        var t = depends.transitions[options.transition],
                            duration = (options.transition==="none" ? null : options.effectDuration);
                        if (duration)
                            $slave.addClass("in-progress");
                        $slave[state ? t.show : t.hide](duration, function() {
                            $slave
                                .removeClass("visible in-progress hidden")
                                .addClass(state ? "visible" : "hidden");
                        });
                    }
                    break;
                case "enable":
                    if (state)
                        depends._enable($slave);
                    else
                        depends._disable($slave);
                    break;
            }
        },

        blockDefault: function(event) {
            event.preventDefault();
        }
    };

    patterns.register(depends);
    return depends; // XXX for tests only
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>