<commit_before>import Ember from 'ember';

import HasItemLists from 'flarum/mixins/has-item-lists';
import NavItem from 'flarum/components/ui/nav-item';
import DropdownSelect from 'flarum/components/ui/dropdown-select';

var precompileTemplate = Ember.Handlebars.compile;

export default Ember.View.extend(HasItemLists, {
  itemLists: ['sidebar'],

  didInsertElement: function() {
    // Affix the sidebar so that when the user scrolls down it will stick
    // to the top of their viewport.
    var $sidebar = this.$('.user-nav');
    $sidebar.find('> ul').affix({
      offset: {
        top: function () {
          return $sidebar.offset().top - $('#header').outerHeight(true) - parseInt($sidebar.css('margin-top'));
        },
        bottom: function () {
          return (this.bottom = $('#footer').outerHeight(true));
        }
      }
    });
  },

  populateSidebar: function(items) {
    var nav = this.populateItemList('nav');
    items.pushObjectWithTag(DropdownSelect.extend({items: nav, listItemClass: 'title-control'}), 'nav');
  },

  populateNav: function(items) {
    items.pushObjectWithTag(NavItem.extend({
      label: 'Activity',
      icon: 'user',
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="")}}{{fa-icon icon}} {{label}}{{/link-to}}')
    }), 'activity');

    items.pushObjectWithTag(NavItem.extend({
      label: 'Discussions',
      icon: 'reorder',
      badge: Ember.computed.alias('controller.model.discussionsCount'),
      controller: this.get('controller'),
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="discussions")}}{{fa-icon icon}} {{label}} {{badge}}{{/link-to}}')
    }), 'discussions');

    items.pushObjectWithTag(NavItem.extend({
      label: 'Posts',
      icon: 'comment-o',
      badge: Ember.computed.alias('controller.model.commentsCount'),
      controller: this.get('controller'),
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="posts")}}{{fa-icon icon}} {{label}} {{badge}}{{/link-to}}')
    }), 'posts');

    this.addSeparatorItem(items);

    if (this.get('controller.model') === this.get('controller.session.user')) {
      items.pushObjectWithTag(NavItem.extend({
        label: 'Settings',
        icon: 'cog',
        layout: precompileTemplate('{{#link-to "user.settings"}}{{fa-icon icon}} {{label}}{{/link-to}}')
      }), 'settings');
    }
  }
});
<commit_msg>Clean up user nav items<commit_after>import Ember from 'ember';

import HasItemLists from 'flarum/mixins/has-item-lists';
import NavItem from 'flarum/components/ui/nav-item';
import DropdownSelect from 'flarum/components/ui/dropdown-select';

var precompileTemplate = Ember.Handlebars.compile;

export default Ember.View.extend(HasItemLists, {
  itemLists: ['sidebar'],

  didInsertElement: function() {
    // Affix the sidebar so that when the user scrolls down it will stick
    // to the top of their viewport.
    var $sidebar = this.$('.user-nav');
    $sidebar.find('> ul').affix({
      offset: {
        top: function () {
          return $sidebar.offset().top - $('#header').outerHeight(true) - parseInt($sidebar.css('margin-top'));
        },
        bottom: function () {
          return (this.bottom = $('#footer').outerHeight(true));
        }
      }
    });
  },

  populateSidebar: function(items) {
    var nav = this.populateItemList('nav');
    items.pushObjectWithTag(DropdownSelect.extend({items: nav, listItemClass: 'title-control'}), 'nav');
  },

  populateNav: function(items) {
    var HasUser = Ember.Mixin.create({
      parentController: this.get('controller'),
      user: Ember.computed.alias('parentController.model')
    });

    items.pushObjectWithTag(NavItem.extend(HasUser, {
      label: 'Activity',
      icon: 'user',
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="")}}{{fa-icon icon}} {{label}}{{/link-to}}')
    }), 'activity');

    items.pushObjectWithTag(NavItem.extend(HasUser, {
      label: 'Discussions',
      icon: 'reorder',
      badge: Ember.computed.alias('user.discussionsCount'),
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="discussions")}}{{fa-icon icon}} {{label}} {{badge}}{{/link-to}}')
    }), 'discussions');

    items.pushObjectWithTag(NavItem.extend(HasUser, {
      label: 'Posts',
      icon: 'comment-o',
      badge: Ember.computed.alias('parentController.model.commentsCount'),
      layout: precompileTemplate('{{#link-to "user.activity" (query-params filter="posts")}}{{fa-icon icon}} {{label}} {{badge}}{{/link-to}}')
    }), 'posts');

    this.addSeparatorItem(items);

    if (this.get('controller.model') === this.get('controller.session.user')) {
      items.pushObjectWithTag(NavItem.extend({
        label: 'Settings',
        icon: 'cog',
        layout: precompileTemplate('{{#link-to "user.settings"}}{{fa-icon icon}} {{label}}{{/link-to}}')
      }), 'settings');
    }
  }
});
<|endoftext|>


<commit_before>const path = require('path')
const { posterImagePath } = require('../../config')
const request = require('../shared/request')
const BASE_URL = 'https://us-central1-test-firebase-functions-82b96.cloudfunctions.net/getMovieMetadata'

module.exports = {
  // External API.
  // Pass in the title of the movie.
  // Returns a promise with metadata on success.
  fetchMovieMetadata: function fetchMovieMetadata (movieFile) {
    return fetchMovieDataInternal(movieFile)
  }
}

// TODO: Television Series Handling
// Look for 'Season' and a number in the parent directory (but not title) and use this to
// prioritize type=series over movies. Could also look for patterns like: S4Ep01 in title?

const dataValidator = (data) => {
  if (data.error) {
    throw new Error(data.message)
  }
}

function fetchMovieDataInternal (movieFile) {
  const url = encodeURI(`${BASE_URL}?file=${movieFile}`)
  return request.getJSON(url, dataValidator)
    .then((metadata) => {
      metadata.fileInfo = [{
        location: movieFile,
        query: metadata.successQuery
      }]
      if (metadata.imgUrl) {
        const { ext } = path.parse(metadata.imgUrl)
        metadata.imgFile = path.join(posterImagePath, `${metadata.imdbID}${ext}`)
      } else {
        metadata.imgFile = ''
      }
      return metadata
    })
}
<commit_msg>URI encode file names before sending to server<commit_after>const path = require('path')
const { posterImagePath } = require('../../config')
const request = require('../shared/request')
const BASE_URL = 'https://us-central1-test-firebase-functions-82b96.cloudfunctions.net/getMovieMetadata'

module.exports = {
  // External API.
  // Pass in the title of the movie.
  // Returns a promise with metadata on success.
  fetchMovieMetadata: function fetchMovieMetadata (movieFile) {
    return fetchMovieDataInternal(movieFile)
  }
}

// TODO: Television Series Handling
// Look for 'Season' and a number in the parent directory (but not title) and use this to
// prioritize type=series over movies. Could also look for patterns like: S4Ep01 in title?

const dataValidator = (data) => {
  if (data.error) {
    throw new Error(data.message)
  }
}

function fetchMovieDataInternal (movieFile) {
  const url = `${BASE_URL}?file=${encodeURIComponent(movieFile)}`
  return request.getJSON(url, dataValidator)
    .then((metadata) => {
      metadata.fileInfo = [{
        location: movieFile,
        query: metadata.successQuery
      }]
      if (metadata.imgUrl) {
        const { ext } = path.parse(metadata.imgUrl)
        metadata.imgFile = path.join(posterImagePath, `${metadata.imdbID}${ext}`)
      } else {
        metadata.imgFile = ''
      }
      return metadata
    })
}
<|endoftext|>


<commit_before>const assert = require('assert')

const remote = require('electron').remote
const ipcRenderer = require('electron').ipcRenderer

const Menu = remote.require('electron').Menu
const MenuItem = remote.require('electron').MenuItem

describe('menu module', function () {
  describe('Menu.buildFromTemplate', function () {
    it('should be able to attach extra fields', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          extra: 'field'
        }
      ])
      assert.equal(menu.items[0].extra, 'field')
    })

    it('does not modify the specified template', function () {
      var template = ipcRenderer.sendSync('eval', "var template = [{label: 'text', submenu: [{label: 'sub'}]}];\nrequire('electron').Menu.buildFromTemplate(template);\ntemplate;")
      assert.deepStrictEqual(template, [
        {
          label: 'text',
          submenu: [
            {
              label: 'sub'
            }
          ]
        }
      ])
    })

    it('does not throw exceptions for undefined/null values', function () {
      assert.doesNotThrow(function () {
        Menu.buildFromTemplate([
          {
            label: 'text',
            accelerator: undefined
          },
          {
            label: 'text again',
            accelerator: null
          }
        ])
      })
    })

    describe('Menu.buildFromTemplate should reorder based on item position specifiers', function () {
      it('should position before existing item', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '2',
            id: '2'
          }, {
            label: '3',
            id: '3'
          }, {
            label: '1',
            id: '1',
            position: 'before=2'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
      })

      it('should position after existing item', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '1',
            id: '1'
          }, {
            label: '3',
            id: '3'
          }, {
            label: '2',
            id: '2',
            position: 'after=1'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
      })

      it('should position at endof existing separator groups', function () {
        var menu = Menu.buildFromTemplate([
          {
            type: 'separator',
            id: 'numbers'
          }, {
            type: 'separator',
            id: 'letters'
          }, {
            label: 'a',
            id: 'a',
            position: 'endof=letters'
          }, {
            label: '1',
            id: '1',
            position: 'endof=numbers'
          }, {
            label: 'b',
            id: 'b',
            position: 'endof=letters'
          }, {
            label: '2',
            id: '2',
            position: 'endof=numbers'
          }, {
            label: 'c',
            id: 'c',
            position: 'endof=letters'
          }, {
            label: '3',
            id: '3',
            position: 'endof=numbers'
          }
        ])
        assert.equal(menu.items[0].id, 'numbers')
        assert.equal(menu.items[1].label, '1')
        assert.equal(menu.items[2].label, '2')
        assert.equal(menu.items[3].label, '3')
        assert.equal(menu.items[4].id, 'letters')
        assert.equal(menu.items[5].label, 'a')
        assert.equal(menu.items[6].label, 'b')
        assert.equal(menu.items[7].label, 'c')
      })

      it('should create separator group if endof does not reference existing separator group', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: 'a',
            id: 'a',
            position: 'endof=letters'
          }, {
            label: '1',
            id: '1',
            position: 'endof=numbers'
          }, {
            label: 'b',
            id: 'b',
            position: 'endof=letters'
          }, {
            label: '2',
            id: '2',
            position: 'endof=numbers'
          }, {
            label: 'c',
            id: 'c',
            position: 'endof=letters'
          }, {
            label: '3',
            id: '3',
            position: 'endof=numbers'
          }
        ])
        assert.equal(menu.items[0].id, 'letters')
        assert.equal(menu.items[1].label, 'a')
        assert.equal(menu.items[2].label, 'b')
        assert.equal(menu.items[3].label, 'c')
        assert.equal(menu.items[4].id, 'numbers')
        assert.equal(menu.items[5].label, '1')
        assert.equal(menu.items[6].label, '2')
        assert.equal(menu.items[7].label, '3')
      })

      it('should continue inserting items at next index when no specifier is present', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '4',
            id: '4'
          }, {
            label: '5',
            id: '5'
          }, {
            label: '1',
            id: '1',
            position: 'before=4'
          }, {
            label: '2',
            id: '2'
          }, {
            label: '3',
            id: '3'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
        assert.equal(menu.items[3].label, '4')
        assert.equal(menu.items[4].label, '5')
      })
    })
  })

  describe('Menu.insert', function () {
    it('should store item in @items by its index', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: '1'
        }, {
          label: '2'
        }, {
          label: '3'
        }
      ])
      var item = new MenuItem({
        label: 'inserted'
      })
      menu.insert(1, item)
      assert.equal(menu.items[0].label, '1')
      assert.equal(menu.items[1].label, 'inserted')
      assert.equal(menu.items[2].label, '2')
      assert.equal(menu.items[3].label, '3')
    })
  })

  describe('MenuItem.click', function () {
    it('should be called with the item object passed', function (done) {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          click: function (item) {
            assert.equal(item.constructor.name, 'MenuItem')
            assert.equal(item.label, 'text')
            done()
          }
        }
      ])
      menu.delegate.executeCommand(menu.items[0].commandId)
    })
  })

  describe('MenuItem with checked property', function () {
    it('clicking an checkbox item should flip the checked property', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          type: 'checkbox'
        }
      ])
      assert.equal(menu.items[0].checked, false)
      menu.delegate.executeCommand(menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
    })

    it('clicking an radio item should always make checked property true', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          type: 'radio'
        }
      ])
      menu.delegate.executeCommand(menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
      menu.delegate.executeCommand(menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
    })

    it('at least have one item checked in each group', function () {
      var i, j, k, menu, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      menu.delegate.menuWillShow()
      assert.equal(menu.items[0].checked, true)
      assert.equal(menu.items[12].checked, true)
    })

    it('should assign groupId automatically', function () {
      var groupId, i, j, k, l, m, menu, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      groupId = menu.items[0].groupId
      for (i = l = 0; l <= 10; i = ++l) {
        assert.equal(menu.items[i].groupId, groupId)
      }
      for (i = m = 12; m <= 20; i = ++m) {
        assert.equal(menu.items[i].groupId, groupId + 1)
      }
    })

    it("setting 'checked' should flip other items' 'checked' property", function () {
      var i, j, k, l, m, menu, n, o, p, q, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      for (i = l = 0; l <= 10; i = ++l) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[0].checked = true
      assert.equal(menu.items[0].checked, true)
      for (i = m = 1; m <= 10; i = ++m) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[10].checked = true
      assert.equal(menu.items[10].checked, true)
      for (i = n = 0; n <= 9; i = ++n) {
        assert.equal(menu.items[i].checked, false)
      }
      for (i = o = 12; o <= 20; i = ++o) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[12].checked = true
      assert.equal(menu.items[10].checked, true)
      for (i = p = 0; p <= 9; i = ++p) {
        assert.equal(menu.items[i].checked, false)
      }
      assert.equal(menu.items[12].checked, true)
      for (i = q = 13; q <= 20; i = ++q) {
        assert.equal(menu.items[i].checked, false)
      }
    })
  })
})
<commit_msg>spec: Fix failing tests of Menu<commit_after>const assert = require('assert')

const remote = require('electron').remote
const ipcRenderer = require('electron').ipcRenderer

const Menu = remote.require('electron').Menu
const MenuItem = remote.require('electron').MenuItem

describe('menu module', function () {
  describe('Menu.buildFromTemplate', function () {
    it('should be able to attach extra fields', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          extra: 'field'
        }
      ])
      assert.equal(menu.items[0].extra, 'field')
    })

    it('does not modify the specified template', function () {
      var template = ipcRenderer.sendSync('eval', "var template = [{label: 'text', submenu: [{label: 'sub'}]}];\nrequire('electron').Menu.buildFromTemplate(template);\ntemplate;")
      assert.deepStrictEqual(template, [
        {
          label: 'text',
          submenu: [
            {
              label: 'sub'
            }
          ]
        }
      ])
    })

    it('does not throw exceptions for undefined/null values', function () {
      assert.doesNotThrow(function () {
        Menu.buildFromTemplate([
          {
            label: 'text',
            accelerator: undefined
          },
          {
            label: 'text again',
            accelerator: null
          }
        ])
      })
    })

    describe('Menu.buildFromTemplate should reorder based on item position specifiers', function () {
      it('should position before existing item', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '2',
            id: '2'
          }, {
            label: '3',
            id: '3'
          }, {
            label: '1',
            id: '1',
            position: 'before=2'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
      })

      it('should position after existing item', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '1',
            id: '1'
          }, {
            label: '3',
            id: '3'
          }, {
            label: '2',
            id: '2',
            position: 'after=1'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
      })

      it('should position at endof existing separator groups', function () {
        var menu = Menu.buildFromTemplate([
          {
            type: 'separator',
            id: 'numbers'
          }, {
            type: 'separator',
            id: 'letters'
          }, {
            label: 'a',
            id: 'a',
            position: 'endof=letters'
          }, {
            label: '1',
            id: '1',
            position: 'endof=numbers'
          }, {
            label: 'b',
            id: 'b',
            position: 'endof=letters'
          }, {
            label: '2',
            id: '2',
            position: 'endof=numbers'
          }, {
            label: 'c',
            id: 'c',
            position: 'endof=letters'
          }, {
            label: '3',
            id: '3',
            position: 'endof=numbers'
          }
        ])
        assert.equal(menu.items[0].id, 'numbers')
        assert.equal(menu.items[1].label, '1')
        assert.equal(menu.items[2].label, '2')
        assert.equal(menu.items[3].label, '3')
        assert.equal(menu.items[4].id, 'letters')
        assert.equal(menu.items[5].label, 'a')
        assert.equal(menu.items[6].label, 'b')
        assert.equal(menu.items[7].label, 'c')
      })

      it('should create separator group if endof does not reference existing separator group', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: 'a',
            id: 'a',
            position: 'endof=letters'
          }, {
            label: '1',
            id: '1',
            position: 'endof=numbers'
          }, {
            label: 'b',
            id: 'b',
            position: 'endof=letters'
          }, {
            label: '2',
            id: '2',
            position: 'endof=numbers'
          }, {
            label: 'c',
            id: 'c',
            position: 'endof=letters'
          }, {
            label: '3',
            id: '3',
            position: 'endof=numbers'
          }
        ])
        assert.equal(menu.items[0].id, 'letters')
        assert.equal(menu.items[1].label, 'a')
        assert.equal(menu.items[2].label, 'b')
        assert.equal(menu.items[3].label, 'c')
        assert.equal(menu.items[4].id, 'numbers')
        assert.equal(menu.items[5].label, '1')
        assert.equal(menu.items[6].label, '2')
        assert.equal(menu.items[7].label, '3')
      })

      it('should continue inserting items at next index when no specifier is present', function () {
        var menu = Menu.buildFromTemplate([
          {
            label: '4',
            id: '4'
          }, {
            label: '5',
            id: '5'
          }, {
            label: '1',
            id: '1',
            position: 'before=4'
          }, {
            label: '2',
            id: '2'
          }, {
            label: '3',
            id: '3'
          }
        ])
        assert.equal(menu.items[0].label, '1')
        assert.equal(menu.items[1].label, '2')
        assert.equal(menu.items[2].label, '3')
        assert.equal(menu.items[3].label, '4')
        assert.equal(menu.items[4].label, '5')
      })
    })
  })

  describe('Menu.insert', function () {
    it('should store item in @items by its index', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: '1'
        }, {
          label: '2'
        }, {
          label: '3'
        }
      ])
      var item = new MenuItem({
        label: 'inserted'
      })
      menu.insert(1, item)
      assert.equal(menu.items[0].label, '1')
      assert.equal(menu.items[1].label, 'inserted')
      assert.equal(menu.items[2].label, '2')
      assert.equal(menu.items[3].label, '3')
    })
  })

  describe('MenuItem.click', function () {
    it('should be called with the item object passed', function (done) {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          click: function (item) {
            assert.equal(item.constructor.name, 'MenuItem')
            assert.equal(item.label, 'text')
            done()
          }
        }
      ])
      menu.delegate.executeCommand({}, menu.items[0].commandId)
    })
  })

  describe('MenuItem with checked property', function () {
    it('clicking an checkbox item should flip the checked property', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          type: 'checkbox'
        }
      ])
      assert.equal(menu.items[0].checked, false)
      menu.delegate.executeCommand({}, menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
    })

    it('clicking an radio item should always make checked property true', function () {
      var menu = Menu.buildFromTemplate([
        {
          label: 'text',
          type: 'radio'
        }
      ])
      menu.delegate.executeCommand({}, menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
      menu.delegate.executeCommand({}, menu.items[0].commandId)
      assert.equal(menu.items[0].checked, true)
    })

    it('at least have one item checked in each group', function () {
      var i, j, k, menu, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      menu.delegate.menuWillShow()
      assert.equal(menu.items[0].checked, true)
      assert.equal(menu.items[12].checked, true)
    })

    it('should assign groupId automatically', function () {
      var groupId, i, j, k, l, m, menu, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      groupId = menu.items[0].groupId
      for (i = l = 0; l <= 10; i = ++l) {
        assert.equal(menu.items[i].groupId, groupId)
      }
      for (i = m = 12; m <= 20; i = ++m) {
        assert.equal(menu.items[i].groupId, groupId + 1)
      }
    })

    it("setting 'checked' should flip other items' 'checked' property", function () {
      var i, j, k, l, m, menu, n, o, p, q, template
      template = []
      for (i = j = 0; j <= 10; i = ++j) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      template.push({
        type: 'separator'
      })
      for (i = k = 12; k <= 20; i = ++k) {
        template.push({
          label: '' + i,
          type: 'radio'
        })
      }
      menu = Menu.buildFromTemplate(template)
      for (i = l = 0; l <= 10; i = ++l) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[0].checked = true
      assert.equal(menu.items[0].checked, true)
      for (i = m = 1; m <= 10; i = ++m) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[10].checked = true
      assert.equal(menu.items[10].checked, true)
      for (i = n = 0; n <= 9; i = ++n) {
        assert.equal(menu.items[i].checked, false)
      }
      for (i = o = 12; o <= 20; i = ++o) {
        assert.equal(menu.items[i].checked, false)
      }
      menu.items[12].checked = true
      assert.equal(menu.items[10].checked, true)
      for (i = p = 0; p <= 9; i = ++p) {
        assert.equal(menu.items[i].checked, false)
      }
      assert.equal(menu.items[12].checked, true)
      for (i = q = 13; q <= 20; i = ++q) {
        assert.equal(menu.items[i].checked, false)
      }
    })
  })
})
<|endoftext|>


<commit_before>var xmlToJSON = {};									// create a namespace

xmlToJSON.Parser = (function() {							// declare the object, Parser

	options = {				// set up the default options
		 parseCDATA : false,		//   extract cdata blocks, results in an array of text and cdata blocks
		 trim : false,			//   trim leading and trailing whitespace in text nodes
		 normalize: true,		//   collapse multiple spaces to single space
		 namespaceKey : 'ns',		//   tag name for namespace objects, default = '_ns'
		 attributeKey : 'at',		//   tag name for the attributes list, default = '_at'
		 textKey : 'text',		//   tag name for text values, default = '_t'
		 valueKey : 'value',		//   tag name for attribute values, default = '_t'
	}

    var parserConstructor = function Parser(opt) {				// with a constructor to return (see end of function)
        if(false === (this instanceof Parser)) {
            return new Parser();						// it doesn't do anything, but gives an object to provide method upon
        }

		//initialize options
		for (key in opt) {
			options[key] = opt[key];
		}
    }

    parserConstructor.prototype.parseString = function (xmlString) {			// such as this one!  add parseString method to the Parser object

			parser = sax.parser(true, {xmlns: true, trim: options.trim, normalize : options.normalize});		// get an instance of the sax.js parser
			stack = [];							// create an empty array to hold and collapse objects

			// sax.js functions begin here
			parser.onopentag = function (node) {
		  		nodeTmp = {};						// node object
		  		data = {}						// node data
		  		name = node.name;					// tag name to use for this node (default)
		  		if (node.uri) {						// test for namespaces
		  			name = node.local;				// switch tag name to local part (no prefixes in tags)
					data[options.namespaceKey] = node.uri;		// set the namespace as well
				}

				if (!isEmpty(node.attributes) ) {			// do we have an attributes?
					data[options.attributeKey] = {};		// create an empty data object for them
					for (attr in node.attributes) {			// iterate over the attribute names
						attrTmp = stack.pop();			// pop the attribute object from the stack
											//	sax.js encounters attributes ahead of open tags
											//	so, we created them in the "onattribute" method
						for(var key in attrTmp) break;		// neat trick to fetch the first tag name
						data[options.attributeKey][key] = attrTmp[key];	// set the attribute
					}
				}
				nodeTmp[name] = data;					// copy the data into the node object
				stack.push(nodeTmp);					// and push it the stack (happens for every element)
			};

			parser.onclosetag = function (name) {
				if (stack.length < 2) {					// make sure there are at least two things on the stack
					return;						// if not, bail
				} else {
					child = stack.pop();				// the most recent item on the stack is a child not
					for(var ckey in child) break;			// get it's tag name

					parent = stack.pop();				// the second item will be it's parent (since we're processing on closetags)
					for(var pkey in parent) break;			// get it's tag name as well

					if ( !(parent[pkey][ckey]) ) {			// if the parent does not contain an item under the child's tag name
						parent[pkey][ckey] = [child[ckey]];	// add it (in an array to support multiple xml elements of the same name)
					} else {					// otherwise
						parent[pkey][ckey].push(child[ckey]);	// push the child object to the existing array of similiarly named children
					}

					stack.push(parent);				// push the updated parent back to the stack
				}
			};

			parser.onattribute = function (attr) {
		  	  		attrTmp = {};					// attribute object
		 			data = {};					// attribute data
		 			name = attr.name;				// tag name to use for this attribute (default)

		 			if (attr.uri) {					// test for namespaces
		 				if (name != 'xmlns') {			// if the attribute is not the default namespace
		 					name = attr.local;		// switch tag name to local part (no prefixes in tags)
						}
						data[options.namespaceKey] = attr.uri;  // set the namespace as well
					}
					data[options.valueKey] = attr.value;		// set the value of the attribute

					attrTmp[name] = data;				// copy the data into the attribute object
					stack.push(attrTmp);				// and push it to the stack
			};

			parser.ontext = function (text) {
				if (stack.length > 0) {					// make sure there is at least one object on the stack
					nodeTmp = stack.pop();				// get the object
					for(var key in nodeTmp) break;			// get it's tag name

					if (options.parseCDATA) {					// if we are parsing cdata blocks
						if (nodeTmp[key][options.textKey]) 	{	 	// and data exists
							nodeTmp[key][options.textKey].push(text);	// append the new data
						} else {
							nodeTmp[key][options.textKey] =
								nodeTmp[key][options.textKey] = [text];	// otherwise, add it
						}
					} else {
						if (nodeTmp[key][options.textKey]) 	{		// if there is already some text (skipping cdata)
							nodeTmp[key][options.textKey] =
								nodeTmp[key][options.textKey]+ text;	// append the new text value
						} else {
							nodeTmp[key][options.textKey] = text;		// otherwise, just add it.
						}
					}
					stack.push(nodeTmp);				// push the updated object back to the stack
				}
			};

			parser.oncdata = function (cdata) {
				if (options.parseCDATA) {
					if (stack.length > 0) {					// make sure there is at least one object on the stack
						nodeTmp = stack.pop();				// get the object
						for(var key in nodeTmp) break;			// get it's tag name
						nodeTmp[key][options.textKey].push(cdata);	// push the cdata value to the text array
						stack.push(nodeTmp);				// push the updated object back to the stack
					}
				}
			};

			parser.onerror = function (error) {
			  console.debug('error ' + error);				// print errors as they occur
			};
			// end of sax.js methods 					// several nodes were not implemented, such as
											//   processing isntructions, cdata, comments, etc...

			function isEmpty(obj) {						// utility method to test for empty objects, e.g.
				for(var prop in obj) {					//   var mtObj = {}
					if(obj.hasOwnProperty(prop))
						return false;
				}
				return true;
			}

			parser.write(xmlString).close();				// launches the sax parser
			return stack[0];						// leaving the result as the only object on the stack
    }

    return parserConstructor;								// returns the Parser object mentioned earlier
}());

// avail - "use or take advantage of (an opportunity or available resource)."
// takes an expression as a string and tests it inside of a try-catch block.
// if the expression succeeds, the expression is evaluated in the current scope and the value returned
// if the expression fails (and throws an error), null will be returned
// this allows compact syntax for nested accessors without a-priori knowledge of the structure
// e.g.
//	  val = avail('xml.a[0].b[0].text') || 'no value found';
function avail(objString) {
	try {
		return eval(objString);
	} catch (err) {
		return null;
	}
};
<commit_msg>new version.  no dependencies.<commit_after>/* Copyright 2013 William Summers, Metatribal Research
 * adapted from https://developer.mozilla.org/en-US/docs/JXON
 *
 * 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.
 */

/**
 * @author William Summers
 *
 */
var xmlToJSON = (function () {

        var options = { // set up the default options
                parseCDATA: true,	// extract cdata and merge with text
                grokAttr: true,		// convert truthy attributes to boolean, etc
                grokText: true,		// convert truthy text to boolean, etc
                normalize: true,	// collapse multiple spaces to single space
                xmlns: false, 		// include namespaces as attribute in output
                namespaceKey: 'ns', 	// tag name for namespace objects
                textKey: 'text', 	// tag name for text values
                valueKey: 'value', 	// tag name for attribute values
                attrKey: 'attr', 	// tag for attr groups
                attrsAsObject: true, 	// if false, key is used as prefix to name, set prefix to '' to merge children and attrs.
                stripAttrPrefix: true, 	// remove namespace prefixes from nodes(el and attr) (set false if you have elements with the same name in different namespaces)
                stripElemPrefix: true, 	// for elements of same name in diff prefixes, you can use the namespaceKey to determine which it is.
                childrenAsArray: true 	// force children into arrays

        };

        var prefixMatch = new RegExp(/(?!xmlns)^.*:/);
        var trimMatch = new RegExp(/^\s+|\s+$/g);

        var grokType = function (sValue) {
                if (/^\s*$/.test(sValue)) {
                        return null;
                }
                if (/^(?:true|false)$/i.test(sValue)) {
                        return sValue.toLowerCase() === "true";
                }
                if (isFinite(sValue)) {
                        return parseFloat(sValue);
                }
                //if (isFinite(Date.parse(sValue))) {
                //		return new Date(sValue);
                //}
                return sValue;
        };

        var parseString = function (xmlString, opt) {
                return this.parseXML(stringToXML(xmlString), opt);
        }

        var parseXML = function (oXMLParent, opt) {

                // initialize options
                for (key in opt) {
                        options[key] = opt[key];
                }

                var vResult = {}, nLength = 0, sCollectedTxt = "";

                // parse namespace information
                if (options.xmlns && oXMLParent.namespaceURI) {
                        vResult[options.namespaceKey] = oXMLParent.namespaceURI;
                }

                // parse attributes
                // using attributes property instead of hasAttributes method to support older browsers
                if (oXMLParent.attributes) {
                        var vAttribs = {};

                        for (nLength; nLength < oXMLParent.attributes.length; nLength++) {
                                oAttrib = oXMLParent.attributes.item(nLength);
                                vContent = {};
                                attribName = '';

                                if (options.stripAttrPrefix) {
                                        attribName = oAttrib.name.replace(prefixMatch, '');

                                } else {
                                        attribName = oAttrib.name;
                                }

                                if (options.grokAttr) {
                                        vContent[options.valueKey] = grokType(oAttrib.value.replace(trimMatch, ''));
                                } else {
                                        vContent[options.valueKey] = oAttrib.value.replace(trimMatch, '');
                                }

                                if (options.xmlns && oAttrib.namespaceURI) {
                                        vContent[options.namespaceKey] = oAttrib.namespaceURI;
                                }

                                if (options.attrsAsObject) { // attributes with same local name must enable prefixes
                                        vAttribs[attribName] = vContent;
                                } else {
                                        vResult[options.attrKey + attribName] = vContent;
                                }
                        }

                        if (options.attrsAsObject) {
                                vResult[options.attrKey] = vAttribs;
                        } else {}
                }

                // iterate over the children
                if (oXMLParent.hasChildNodes()) {
                        for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) {
                                oNode = oXMLParent.childNodes.item(nItem);

                                if (oNode.nodeType === 4 && options.parseCDATA) {
                                        sCollectedTxt += oNode.nodeValue;
                                } /* nodeType is "CDATASection" (4) */
                                else if (oNode.nodeType === 3) {
                                        sCollectedTxt += oNode.nodeValue;
                                } /* nodeType is "Text" (3) */
                                else if (oNode.nodeType === 1) { /* nodeType is "Element" (1) */

                                        if (nLength === 0) {
                                                vResult = {};
                                        }

                                        // using nodeName to support browser (IE) implementation with no 'localName' property
                                        if (options.stripElemPrefix) {
                                                sProp = oNode.nodeName.replace(prefixMatch, '');
                                        } else {
                                                sProp = oNode.nodeName;
                                        }

                                        vContent = parseXML(oNode);

                                        if (vResult.hasOwnProperty(sProp)) {
                                                if (vResult[sProp].constructor !== Array) {
                                                        vResult[sProp] = [vResult[sProp]];
                                                }
                                                vResult[sProp].push(vContent);

                                        } else {
                                                if (options.childrenAsArray) {
                                                        vResult[sProp] = [];
                                                        vResult[sProp].push(vContent);
                                                } else {
                                                        vResult[sProp] = vContent;
                                                }
                                                nLength++;
                                        }
                                }
                        }
                }

                if (sCollectedTxt) {
                        if (options.grokText) {
                                vResult[options.textKey] = grokType(sCollectedTxt.replace(trimMatch, ''));
                        } else if (options.normalize) {
                                vResult[options.textKey] = sCollectedTxt.replace(trimMatch, '').replace(/\s+/g, " ");
                        } else {
                                vResult[options.textKey] = sCollectedTxt.replace(trimMatch, '');
                        }
                }

                return vResult;
        }

        // Convert xmlDocument to a string
        // Returns null on failure
        var xmlToString = function (xmlDoc) {
                try {
                        var xmlString = xmlDoc.xml ? xmlDoc.xml : (new XMLSerializer()).serializeToString(xmlDoc);
                        return xmlString;
                } catch (err) {
                        return null;
                }
        }

        // Convert a string to XML Node Structure
        // Returns null on failure
        var stringToXML = function (xmlString) {
                try {
                        var xmlDoc = null;

                        if (window.DOMParser) {

                                var parser = new DOMParser();
                                xmlDoc = parser.parseFromString(xmlString, "text/xml");

                                return xmlDoc;
                        } else {
                                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                                xmlDoc.async = false;
                                xmlDoc.loadXML(xmlString);

                                return xmlDoc;
                        }
                } catch (e) {
                        return null;
                }
        }

        // this is the "revealed"/public part of the module
        return {
                parseXML: parseXML,
                parseString: parseString,
        };

}());
<|endoftext|>


<commit_before>var _ = require('node_modules/underscore/underscore.js');
var MessageListView = require('js/message_list_view.js');

add_dependencies({
    XDate: 'node_modules/xdate/src/xdate.js',
    util: 'js/util.js',
});

set_global('page_params', {
  twenty_four_hour_time: false,
});
set_global('home_msg_list', null);
set_global('feature_flags', {twenty_four_hour_time: false});
set_global('people', {small_avatar_url: function () { return ''; }});
set_global('unread', {message_unread: function () {}});
// timerender calls setInterval when imported
set_global('timerender', {
    render_date: function (time1, time2) {
        if (time2 === undefined) {
            return [{outerHTML: String(time1.getTime())}];
        }
        return [{outerHTML: String(time1.getTime()) + ' - ' + String(time2.getTime())}];
    },
});

(function test_merge_message_groups() {
    // MessageListView has lots of DOM code, so we are going to test the message
    // group mearging logic on its own.

    function build_message_context(message, message_context) {
        if (message_context === undefined) {
            message_context = {};
        }
        if (message === undefined) {
            message = {};
        }
        message_context = _.defaults(message_context, {
            include_sender: true,
        });
        message_context.msg = _.defaults(message, {
            id: _.uniqueId('test_message_'),
            status_message: false,
            type: 'stream',
            stream: 'Test Stream 1',
            subject: 'Test Subject 1',
            sender_email: 'test@example.com',
            timestamp: _.uniqueId(),
        });
        return message_context;
    }

    function build_message_group(messages) {
        return {
            message_containers: messages,
            message_group_id: _.uniqueId('test_message_group_'),
            show_date: true,
        };
    }

    function build_list(message_groups) {
        var list = new MessageListView(undefined, undefined, true);
        list._message_groups = message_groups;
        list.list = {
            unsubscribed_bookend_content: function () {},
            subscribed_bookend_content: function () {},
        };
        return list;
    }

    function assert_message_list_equal(list1, list2) {
        assert.deepEqual(
            _.chain(list1).pluck('msg').pluck('id').value(),
            _.chain(list2).pluck('msg').pluck('id').value());
    }

    function assert_message_groups_list_equal(list1, list2) {
        function extract_message_ids(message_group) {
            return _.chain(message_group.messages)
                .pluck('msg')
                .pluck('id')
                .value();
        }
        assert.deepEqual(
            _.map(list1, extract_message_ids),
            _.map(list2, extract_message_ids));
    }

    (function test_empty_list_bottom() {
        var list = build_list([]);
        var message_group = build_message_group([
            build_message_context(),
        ]);

        var result = list.merge_message_groups([message_group], 'bottom');

        assert_message_groups_list_equal(list._message_groups, [message_group]);
        assert_message_groups_list_equal(result.append_groups, [message_group]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_same_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context();
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message1, message2])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, [message2]);
        assert_message_list_equal(result.rerender_messages, [message1]);
    }());

    (function test_append_message_diffrent_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({subject: 'Test subject 2'});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(!message_group2.show_date);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_diffrent_day() {

        var message1 = build_message_context({timestamp: 1000});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({timestamp: 900000});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message_group2.show_date);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_historical() {

        var message1 = build_message_context({historical: false});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({historical: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message_group2.bookend_top);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_same_subject_me_message() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({is_me_message: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message2.include_sender);
        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message1, message2])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, [message2]);
        assert_message_list_equal(result.rerender_messages, [message1]);
    }());

    (function test_prepend_message_same_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context();
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message2, message1])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups,
            [build_message_group([message2, message1])]);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_diffrent_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({subject: 'Test Subject 2'});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_diffrent_day() {

        var message1 = build_message_context({timestamp: 900000});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({timestamp: 1000});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert.equal(
            message_group1.show_date,
            '900000000 - 1000000');
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, [message_group1]);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_historical() {

        var message1 = build_message_context({historical: false});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({historical: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert(message_group1.bookend_top);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

}());
<commit_msg>node tests: Use zrequire in message_list_view.js.<commit_after>zrequire('util');
zrequire('XDate', 'node_modules/xdate/src/xdate');
var MessageListView = zrequire('message_list_view');

set_global('page_params', {
  twenty_four_hour_time: false,
});
set_global('home_msg_list', null);
set_global('feature_flags', {twenty_four_hour_time: false});
set_global('people', {small_avatar_url: function () { return ''; }});
set_global('unread', {message_unread: function () {}});
// timerender calls setInterval when imported
set_global('timerender', {
    render_date: function (time1, time2) {
        if (time2 === undefined) {
            return [{outerHTML: String(time1.getTime())}];
        }
        return [{outerHTML: String(time1.getTime()) + ' - ' + String(time2.getTime())}];
    },
});

(function test_merge_message_groups() {
    // MessageListView has lots of DOM code, so we are going to test the message
    // group mearging logic on its own.

    function build_message_context(message, message_context) {
        if (message_context === undefined) {
            message_context = {};
        }
        if (message === undefined) {
            message = {};
        }
        message_context = _.defaults(message_context, {
            include_sender: true,
        });
        message_context.msg = _.defaults(message, {
            id: _.uniqueId('test_message_'),
            status_message: false,
            type: 'stream',
            stream: 'Test Stream 1',
            subject: 'Test Subject 1',
            sender_email: 'test@example.com',
            timestamp: _.uniqueId(),
        });
        return message_context;
    }

    function build_message_group(messages) {
        return {
            message_containers: messages,
            message_group_id: _.uniqueId('test_message_group_'),
            show_date: true,
        };
    }

    function build_list(message_groups) {
        var list = new MessageListView(undefined, undefined, true);
        list._message_groups = message_groups;
        list.list = {
            unsubscribed_bookend_content: function () {},
            subscribed_bookend_content: function () {},
        };
        return list;
    }

    function assert_message_list_equal(list1, list2) {
        assert.deepEqual(
            _.chain(list1).pluck('msg').pluck('id').value(),
            _.chain(list2).pluck('msg').pluck('id').value());
    }

    function assert_message_groups_list_equal(list1, list2) {
        function extract_message_ids(message_group) {
            return _.chain(message_group.messages)
                .pluck('msg')
                .pluck('id')
                .value();
        }
        assert.deepEqual(
            _.map(list1, extract_message_ids),
            _.map(list2, extract_message_ids));
    }

    (function test_empty_list_bottom() {
        var list = build_list([]);
        var message_group = build_message_group([
            build_message_context(),
        ]);

        var result = list.merge_message_groups([message_group], 'bottom');

        assert_message_groups_list_equal(list._message_groups, [message_group]);
        assert_message_groups_list_equal(result.append_groups, [message_group]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_same_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context();
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message1, message2])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, [message2]);
        assert_message_list_equal(result.rerender_messages, [message1]);
    }());

    (function test_append_message_diffrent_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({subject: 'Test subject 2'});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(!message_group2.show_date);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_diffrent_day() {

        var message1 = build_message_context({timestamp: 1000});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({timestamp: 900000});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message_group2.show_date);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_historical() {

        var message1 = build_message_context({historical: false});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({historical: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message_group2.bookend_top);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group1, message_group2]);
        assert_message_groups_list_equal(result.append_groups, [message_group2]);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_append_message_same_subject_me_message() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({is_me_message: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'bottom');

        assert(message2.include_sender);
        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message1, message2])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, [message2]);
        assert_message_list_equal(result.rerender_messages, [message1]);
    }());

    (function test_prepend_message_same_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context();
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert_message_groups_list_equal(
            list._message_groups,
            [build_message_group([message2, message1])]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, []);
        assert_message_groups_list_equal(result.rerender_groups,
            [build_message_group([message2, message1])]);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_diffrent_subject() {

        var message1 = build_message_context();
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({subject: 'Test Subject 2'});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_diffrent_day() {

        var message1 = build_message_context({timestamp: 900000});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({timestamp: 1000});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert.equal(
            message_group1.show_date,
            '900000000 - 1000000');
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, [message_group1]);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

    (function test_prepend_message_historical() {

        var message1 = build_message_context({historical: false});
        var message_group1 = build_message_group([
            message1,
        ]);

        var message2 = build_message_context({historical: true});
        var message_group2 = build_message_group([
            message2,
        ]);

        var list = build_list([message_group1]);
        var result = list.merge_message_groups([message_group2], 'top');

        assert(message_group1.bookend_top);
        assert_message_groups_list_equal(
            list._message_groups,
            [message_group2, message_group1]);
        assert_message_groups_list_equal(result.append_groups, []);
        assert_message_groups_list_equal(result.prepend_groups, [message_group2]);
        assert_message_groups_list_equal(result.rerender_groups, []);
        assert_message_list_equal(result.append_messages, []);
        assert_message_list_equal(result.rerender_messages, []);
    }());

}());
<|endoftext|>


<commit_before>(function() {
    var getValidMethod = function(method) {
            if (method) {
                var proposedMethod = method.toUpperCase();

                if (['GET', 'POST', 'PUT'].indexOf(proposedMethod) >= 0) {
                    return proposedMethod;
                }
            }
        },

        // NOTE: Safari doesn't have any visual indications when submit is blocked
        interceptSubmit = function() {
            // Intercept submit event
            this.addEventListener('submit', function(event) {
                // Stop form submission.  Believe it or not,
                // both of these are required for some reason,
                // and returning false doesn't seem to reliably work.
                event.preventDefault();
                event.stopPropagation();

                // respect any field validation attributes
                if (this.checkValidity()) {
                    this.fire('submitting');
                    if ('multipart/form-data' !== this.enctype) {
                        sendUrlencodedForm.call(this, this);
                    }
                    else {
                        if ('GET' === this.acceptableMethod) {
                            sendUrlencodedForm.call(this, this);
                        }
                        else {
                            sendMultipartForm.call(this, this);
                        }
                    }
                }
            }.bind(this));

            // Intercept native form submit function.
            // In order to force the browser to highlight the invalid fields,
            // we need to create a hidden submit button and click it if the form is invalid.
            var fakeSubmitEl = document.createElement('input');
            fakeSubmitEl.setAttribute('type', 'submit');
            fakeSubmitEl.style.display = 'none';
            this.appendChild(fakeSubmitEl);
            this.submit = function() {
                if (this.checkValidity()) {
                    this.fire('submit');
                }
                else {
                    fakeSubmitEl.click();
                }
            };
        },

        listenForAjaxComplete = function() {
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0];

            sender.addEventListener('core-complete', function(event) {
                 this.fire('submitted', event.detail.xhr);
            }.bind(this));
        },

        maybeParseCoreDropdownMenu = function(customElement, data) {
            if (customElement.tagName.toLowerCase() === 'core-dropdown-menu' ||
                customElement.tagName.toLowerCase() === 'paper-dropdown-menu') {

                var coreMenu = customElement.getElementsByTagName('core-menu')[0],
                    selectedItem = coreMenu && coreMenu.selectedItem;

                if (selectedItem) {
                    data[customElement.getAttribute('name')] = selectedItem.label || selectedItem.innerText;
                    return true;
                }
            }
        },

        maybeParseFileInput = function(customElement, data) {
            if (customElement.tagName.toLowerCase() === 'file-input') {
                var fileInputName = customElement.getAttribute('name') || 'files';

                if (customElement.files.length > 1) {
                    fileInputName += '[]';
                }

                customElement.files.forEach(function(file) {
                    data[fileInputName] = file;
                });

                return !!customElement.files.length;
            }
        },

        maybeParseGenericCustomElement = function(customElement, data) {
            if (customElement.tagName.indexOf('-') >= 0 && customElement.value != null) {
                data[customElement.getAttribute('name')] = customElement.value;
                return true;
            }
        },

        parseCustomElements = function(form, parseFileInputs) {
            var data = {};

            Array.prototype.slice.call(form.querySelectorAll('*[name]')).forEach(function(el) {
                (parseFileInputs && maybeParseFileInput(el, data)) ||
                maybeParseCoreDropdownMenu(el, data) ||
                maybeParseGenericCustomElement(el, data);
            });

            return data;
        },

        // @TODO: should these FormData parse methods be exposed as events
        // if, say, someone wanted to filter or transform the data in a form
        // (i.e., radio from yes/no to true/false, or textarea from markdown to
        // html)?
        parseFormData = function(form) {
            var formData = new FormData(form),
                customElementData = parseCustomElements(form, true);

            if (customElementData) {
                Object.keys(customElementData).forEach(function(fieldName) {
                    formData.append(fieldName, customElementData[fieldName]);
                });
            }

            return formData;

        },

        /**
         * Parse an `HTMLRadioElement`'s value, returning the value iff
         * the element has a present `checked` attribute.
         *
         * @param HTMLRadioElement element
         * @return mixed The element's value
         */
        parseRadioElementValue = function(element) {
            var value;
            if (element.checked === true) {
                value = element.value;
            }
            return value;

        },

        /**
         * Parse an `HTMLOptionElement`'s value, returning the value iff
         * the element has a present `selected` attribute.
         * @param HTMLOptionElement element
         * @return mixed The element's value
         */
        parseSelectOptionElementValue = function(element) {
            var elementValue;
            if (element.selected === true){
                elementValue = element.value;
            }
            return elementValue;
        },

        /**
         * Parse an `HTMLOptGroupElement` return the `HTMLOptionElement` that
         * has a `checked` attribute.
         * @param HTMLOptGroupElement element
         * @return mixed The element's value
         */
        parseSelectOptgroupElementValue = function(element) {
            var elementValue;
            Array.prototype.forEach.call(element.options, function(optionElement){
                var tempElementValue = parseSelectOptionElementValue(optionElement);
                if (tempElementValue){
                    elementValue = tempElementValue;
                }
            });
            return elementValue;
        },

        /**
         * Parse an `HTMLSelectElement`'s value.
         *
         * @param HTMLSelectElement element
         * @return mixed The element's selected values
         */
        parseSelectElementValues = function(element) {
            var elementValues = [];

            Array.prototype.forEach.call(element.options, function(optionElement){
                var tempElementValue = parseSelectOptionElementValue(optionElement);
                tempElementValue && elementValues.push(tempElementValue);
            });

            return elementValues;
        },

        /**
         * Parse an `HTMLInputElement`'s value.
         * @param HTMLInputElement element
         * @return mixed The element's value
         */
        parseInputElementValue = function(element){
            var elementValue,
                elementType = element.type;

            if (element.disabled === true ||
                ['submit', 'reset', 'button', 'image'].indexOf(elementType) !== -1) {
                 // do nothing for these button types
            }
            else if (elementType === 'radio') {
                elementValue = parseRadioElementValue(element);
            } else {
                elementValue = element.value;
            }

            return elementValue;
        },

        /**
         * Return the value of some `HTMLElement`s  value attribute if possible.
         * @param HTMLElement element
         * @return mixed The element's value attribute
         */
        parseElementValue = function(element){
            var elementValue,
                elementTag = element.tagName.toLowerCase();

            if (elementTag === 'input') {
                elementValue = parseInputElementValue(element);
            }
            else if (elementTag === 'textarea'){
                elementValue = element.value;
            }
            //else if(/select*/.exec(elementTag)) {
            else if (elementTag === 'select') {
                 elementValue = parseSelectElementValues(element);
            }

            return elementValue;

        },

        /**
         * Parse an `HTMLFormElement` into key value pairs
         * @param HTMLFormElement form
         * @return Object key, value pairs representing the html form
         */
        parseForm = function(form) {
            var formObj = {},
                formElements = form.getElementsByTagName('input'),
                customElementsData = parseCustomElements(form);

            formElements = Array.prototype.slice.call(formElements);
            formElements = formElements.concat(Array.prototype.slice.call(form.getElementsByTagName('select')));
            formElements = formElements.concat(Array.prototype.slice.call(form.getElementsByTagName('textarea')));

            formElements.forEach(function(formElement){
                var key = formElement.name,
                    val = parseElementValue(formElement);

                if (key && val) {
                    formObj[key] = val;
                }
            });

            Object.keys(customElementsData).forEach(function(fieldName) {
                formObj[fieldName] = customElementsData[fieldName];
            });

            return formObj;
        },

        /**
         * Send a url-encoded `HTMLFormElement` in the URL query string.
         * @param HTMLFormElement form
         */
        sendUrlencodedForm = function(form){
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0],
                // We must URL encode the data and place it in the body or
                // query paramter section of the URI (depending on the method).
                // core-ajax attempts to do this for us, but this requires we pass
                // an Object to core-ajax with the params and we cannot properly
                // express multiple values for a <select> (which is possible)
                // via a JavaScript Object.
                data = toQueryString(parseForm(form));

            if (this.cookies) {
                sender.withCredentials = true;
            }

            if (this.acceptableMethod === 'POST') {
                sender.body = data;
            }
            else {
                sender.url += (sender.url.indexOf('?') > 0 ? '&' : '?') + data;
            }

            sender.go();
        },

        /**
         * Send a multipart-encoded `HTMLFormElement` in the request body.
         * @param HTMLFormElement form
         */
        sendMultipartForm = function(form) {
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0],
                data = parseFormData(form);

            // make sure Polymer/core-ajax doesn't touch the Content-Type.
            // The browser must set this with the proper multipart boundary ID.
            sender.contentType = null;

            if (this.cookies) {
                sender.withCredentials = true;
            }

            sender.body = data;
            sender.go();
        },

        toQueryString = function(params) {
            var queryParams = [];

            Object.keys(params).forEach(function(key) {
                var val = params[key];
                key = encodeURIComponent(key);

                if (val && Object.prototype.toString.call(val) === '[object Array]') {
                    val.forEach(function(valInArray) {
                        queryParams.push(key + '=' + encodeURIComponent(valInArray));
                    });
                }
                else {
                    queryParams.push(val == null ? key : (key + '=' + encodeURIComponent(val)));
                }
            });

            return queryParams.join('&');
          },

        watchForInvalidFields = function(form) {
            var customEl = this,
                fields = form.getElementsByTagName('input'),
                invalidFields = [],
                timer = null;

            fields = Array.prototype.slice.call(fields);
            fields = fields.concat(Array.prototype.slice.call(form.getElementsByTagName('select')));
            fields = fields.concat(Array.prototype.slice.call(form.getElementsByTagName('textarea')));

            fields.forEach(function(field) {
                field.addEventListener('invalid', function() {
                    invalidFields.push(this);

                    // In case another element is invalid and the event
                    // hasn't been triggered yet, hold off on firing the
                    // invalid event on the custom el.
                    clearTimeout(timer);
                    timer = setTimeout(function() {
                        customEl.fire('invalid', invalidFields);
                        invalidFields = [];
                        console.error('Form submission blocked - constraints violation.');
                    }, 10);
                });
            });
        };

    this.ajaxForm = {
       /**
        * Fired when a response is received.
        *
        * @event core-response
        */
        cookies: false,

        domReady: function() {
            // The method attribute set on the light-DOM `<form>`
            // can't seem to be accessed as a property of this element,
            // unlike other attributes.  Perhaps due to the fact that
            // we are extending a form and a "natural" form also has a
            // method attr?  Perhaps something special about this attr?
            // Need to look into this further.
            this.acceptableMethod = getValidMethod(this.getAttribute('method'));

            if (!this.acceptableMethod) {
                throw new Error('Invalid method!');
            }

            watchForInvalidFields.call(this, this);

            interceptSubmit.call(this);

            listenForAjaxComplete.call(this);
        }
    };
}());
<commit_msg>fix(paper-dropdown-menu): now make it work in Firefox too fixes #25<commit_after>(function() {
    var getValidMethod = function(method) {
            if (method) {
                var proposedMethod = method.toUpperCase();

                if (['GET', 'POST', 'PUT'].indexOf(proposedMethod) >= 0) {
                    return proposedMethod;
                }
            }
        },

        // NOTE: Safari doesn't have any visual indications when submit is blocked
        interceptSubmit = function() {
            // Intercept submit event
            this.addEventListener('submit', function(event) {
                // Stop form submission.  Believe it or not,
                // both of these are required for some reason,
                // and returning false doesn't seem to reliably work.
                event.preventDefault();
                event.stopPropagation();

                // respect any field validation attributes
                if (this.checkValidity()) {
                    this.fire('submitting');
                    if ('multipart/form-data' !== this.enctype) {
                        sendUrlencodedForm.call(this, this);
                    }
                    else {
                        if ('GET' === this.acceptableMethod) {
                            sendUrlencodedForm.call(this, this);
                        }
                        else {
                            sendMultipartForm.call(this, this);
                        }
                    }
                }
            }.bind(this));

            // Intercept native form submit function.
            // In order to force the browser to highlight the invalid fields,
            // we need to create a hidden submit button and click it if the form is invalid.
            var fakeSubmitEl = document.createElement('input');
            fakeSubmitEl.setAttribute('type', 'submit');
            fakeSubmitEl.style.display = 'none';
            this.appendChild(fakeSubmitEl);
            this.submit = function() {
                if (this.checkValidity()) {
                    this.fire('submit');
                }
                else {
                    fakeSubmitEl.click();
                }
            };
        },

        listenForAjaxComplete = function() {
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0];

            sender.addEventListener('core-complete', function(event) {
                 this.fire('submitted', event.detail.xhr);
            }.bind(this));
        },

        maybeParseCoreDropdownMenu = function(customElement, data) {
            if (customElement.tagName.toLowerCase() === 'core-dropdown-menu' ||
                customElement.tagName.toLowerCase() === 'paper-dropdown-menu') {

                var coreMenu = customElement.getElementsByTagName('core-menu')[0],
                    selectedItem = coreMenu && coreMenu.selectedItem;

                if (selectedItem) {
                    data[customElement.getAttribute('name')] = selectedItem.label || selectedItem.textContent;
                    return true;
                }
            }
        },

        maybeParseFileInput = function(customElement, data) {
            if (customElement.tagName.toLowerCase() === 'file-input') {
                var fileInputName = customElement.getAttribute('name') || 'files';

                if (customElement.files.length > 1) {
                    fileInputName += '[]';
                }

                customElement.files.forEach(function(file) {
                    data[fileInputName] = file;
                });

                return !!customElement.files.length;
            }
        },

        maybeParseGenericCustomElement = function(customElement, data) {
            if (customElement.tagName.indexOf('-') >= 0 && customElement.value != null) {
                data[customElement.getAttribute('name')] = customElement.value;
                return true;
            }
        },

        parseCustomElements = function(form, parseFileInputs) {
            var data = {};

            Array.prototype.slice.call(form.querySelectorAll('*[name]')).forEach(function(el) {
                (parseFileInputs && maybeParseFileInput(el, data)) ||
                maybeParseCoreDropdownMenu(el, data) ||
                maybeParseGenericCustomElement(el, data);
            });

            return data;
        },

        // @TODO: should these FormData parse methods be exposed as events
        // if, say, someone wanted to filter or transform the data in a form
        // (i.e., radio from yes/no to true/false, or textarea from markdown to
        // html)?
        parseFormData = function(form) {
            var formData = new FormData(form),
                customElementData = parseCustomElements(form, true);

            if (customElementData) {
                Object.keys(customElementData).forEach(function(fieldName) {
                    formData.append(fieldName, customElementData[fieldName]);
                });
            }

            return formData;

        },

        /**
         * Parse an `HTMLRadioElement`'s value, returning the value iff
         * the element has a present `checked` attribute.
         *
         * @param HTMLRadioElement element
         * @return mixed The element's value
         */
        parseRadioElementValue = function(element) {
            var value;
            if (element.checked === true) {
                value = element.value;
            }
            return value;

        },

        /**
         * Parse an `HTMLOptionElement`'s value, returning the value iff
         * the element has a present `selected` attribute.
         * @param HTMLOptionElement element
         * @return mixed The element's value
         */
        parseSelectOptionElementValue = function(element) {
            var elementValue;
            if (element.selected === true){
                elementValue = element.value;
            }
            return elementValue;
        },

        /**
         * Parse an `HTMLOptGroupElement` return the `HTMLOptionElement` that
         * has a `checked` attribute.
         * @param HTMLOptGroupElement element
         * @return mixed The element's value
         */
        parseSelectOptgroupElementValue = function(element) {
            var elementValue;
            Array.prototype.forEach.call(element.options, function(optionElement){
                var tempElementValue = parseSelectOptionElementValue(optionElement);
                if (tempElementValue){
                    elementValue = tempElementValue;
                }
            });
            return elementValue;
        },

        /**
         * Parse an `HTMLSelectElement`'s value.
         *
         * @param HTMLSelectElement element
         * @return mixed The element's selected values
         */
        parseSelectElementValues = function(element) {
            var elementValues = [];

            Array.prototype.forEach.call(element.options, function(optionElement){
                var tempElementValue = parseSelectOptionElementValue(optionElement);
                tempElementValue && elementValues.push(tempElementValue);
            });

            return elementValues;
        },

        /**
         * Parse an `HTMLInputElement`'s value.
         * @param HTMLInputElement element
         * @return mixed The element's value
         */
        parseInputElementValue = function(element){
            var elementValue,
                elementType = element.type;

            if (element.disabled === true ||
                ['submit', 'reset', 'button', 'image'].indexOf(elementType) !== -1) {
                 // do nothing for these button types
            }
            else if (elementType === 'radio') {
                elementValue = parseRadioElementValue(element);
            } else {
                elementValue = element.value;
            }

            return elementValue;
        },

        /**
         * Return the value of some `HTMLElement`s  value attribute if possible.
         * @param HTMLElement element
         * @return mixed The element's value attribute
         */
        parseElementValue = function(element){
            var elementValue,
                elementTag = element.tagName.toLowerCase();

            if (elementTag === 'input') {
                elementValue = parseInputElementValue(element);
            }
            else if (elementTag === 'textarea'){
                elementValue = element.value;
            }
            //else if(/select*/.exec(elementTag)) {
            else if (elementTag === 'select') {
                 elementValue = parseSelectElementValues(element);
            }

            return elementValue;

        },

        /**
         * Parse an `HTMLFormElement` into key value pairs
         * @param HTMLFormElement form
         * @return Object key, value pairs representing the html form
         */
        parseForm = function(form) {
            var formObj = {},
                formElements = form.getElementsByTagName('input'),
                customElementsData = parseCustomElements(form);

            formElements = Array.prototype.slice.call(formElements);
            formElements = formElements.concat(Array.prototype.slice.call(form.getElementsByTagName('select')));
            formElements = formElements.concat(Array.prototype.slice.call(form.getElementsByTagName('textarea')));

            formElements.forEach(function(formElement){
                var key = formElement.name,
                    val = parseElementValue(formElement);

                if (key && val) {
                    formObj[key] = val;
                }
            });

            Object.keys(customElementsData).forEach(function(fieldName) {
                formObj[fieldName] = customElementsData[fieldName];
            });

            return formObj;
        },

        /**
         * Send a url-encoded `HTMLFormElement` in the URL query string.
         * @param HTMLFormElement form
         */
        sendUrlencodedForm = function(form){
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0],
                // We must URL encode the data and place it in the body or
                // query paramter section of the URI (depending on the method).
                // core-ajax attempts to do this for us, but this requires we pass
                // an Object to core-ajax with the params and we cannot properly
                // express multiple values for a <select> (which is possible)
                // via a JavaScript Object.
                data = toQueryString(parseForm(form));

            if (this.cookies) {
                sender.withCredentials = true;
            }

            if (this.acceptableMethod === 'POST') {
                sender.body = data;
            }
            else {
                sender.url += (sender.url.indexOf('?') > 0 ? '&' : '?') + data;
            }

            sender.go();
        },

        /**
         * Send a multipart-encoded `HTMLFormElement` in the request body.
         * @param HTMLFormElement form
         */
        sendMultipartForm = function(form) {
            var sender = this.shadowRoot.getElementsByTagName('core-ajax')[0],
                data = parseFormData(form);

            // make sure Polymer/core-ajax doesn't touch the Content-Type.
            // The browser must set this with the proper multipart boundary ID.
            sender.contentType = null;

            if (this.cookies) {
                sender.withCredentials = true;
            }

            sender.body = data;
            sender.go();
        },

        toQueryString = function(params) {
            var queryParams = [];

            Object.keys(params).forEach(function(key) {
                var val = params[key];
                key = encodeURIComponent(key);

                if (val && Object.prototype.toString.call(val) === '[object Array]') {
                    val.forEach(function(valInArray) {
                        queryParams.push(key + '=' + encodeURIComponent(valInArray));
                    });
                }
                else {
                    queryParams.push(val == null ? key : (key + '=' + encodeURIComponent(val)));
                }
            });

            return queryParams.join('&');
          },

        watchForInvalidFields = function(form) {
            var customEl = this,
                fields = form.getElementsByTagName('input'),
                invalidFields = [],
                timer = null;

            fields = Array.prototype.slice.call(fields);
            fields = fields.concat(Array.prototype.slice.call(form.getElementsByTagName('select')));
            fields = fields.concat(Array.prototype.slice.call(form.getElementsByTagName('textarea')));

            fields.forEach(function(field) {
                field.addEventListener('invalid', function() {
                    invalidFields.push(this);

                    // In case another element is invalid and the event
                    // hasn't been triggered yet, hold off on firing the
                    // invalid event on the custom el.
                    clearTimeout(timer);
                    timer = setTimeout(function() {
                        customEl.fire('invalid', invalidFields);
                        invalidFields = [];
                        console.error('Form submission blocked - constraints violation.');
                    }, 10);
                });
            });
        };

    this.ajaxForm = {
       /**
        * Fired when a response is received.
        *
        * @event core-response
        */
        cookies: false,

        domReady: function() {
            // The method attribute set on the light-DOM `<form>`
            // can't seem to be accessed as a property of this element,
            // unlike other attributes.  Perhaps due to the fact that
            // we are extending a form and a "natural" form also has a
            // method attr?  Perhaps something special about this attr?
            // Need to look into this further.
            this.acceptableMethod = getValidMethod(this.getAttribute('method'));

            if (!this.acceptableMethod) {
                throw new Error('Invalid method!');
            }

            watchForInvalidFields.call(this, this);

            interceptSubmit.call(this);

            listenForAjaxComplete.call(this);
        }
    };
}());
<|endoftext|>