<commit_before>//= require_tree ./templates

var App = Ember.Application.create({
  rootElement: '#builds-application'
});

App.Router.map(function() {
  this.route('release');
  this.route('beta');
  this.route('canary');
  this.route('tagged');
});

App.Router.reopen({
  notifyGoogleAnalytics: function() {
    var url = this.get('url');

    // Add a slash if neccesary
    if (!/^\//.test(url)){ url = '/' + url; }

    _gaq.push(['_trackPageview', '/builds' + url]);
  }.on('didTransition')
});

App.CopyClipboardComponent = Ember.Component.extend({
  tagName: 'span',
  hasFlash: ZeroClipboard.detectFlashSupport(),

  didInsertElement: function () {
    var clip = new ZeroClipboard(this.$('button'), {
      // This would normally be a relative path
      moviePath: "/images/ZeroClipboard.swf",

      trustedDomains: ["*"],
      allowScriptAccess: "always"
    });

    clip.on('mousedown', function(client, e) {
      Em.run.later(this, function() {
        $(this).removeClass('loading');
        $(this).removeAttr('disabled');
      }, 1000);
      Em.run.next(this, function() {
        $(this).addClass('loading');
        $(this).attr('disabled', 'disabled');
      });
    });

    this.$('input').on('click', function() {
      $(this).select();
    });
  }
});

App.S3Bucket = Ember.Object.extend({
  files: [],
  isLoading: false,

  // Setup these as default values.
  // They can be overridden on create.
  queryUseSSL: true,
  objectUseSSL: false,
  delimiter: '/',
  bucket: 'builds.emberjs.com',
  endpoint: 's3.amazonaws.com',

  delimiterParameter: function(){
    var delimiter = this.getWithDefault('delimiter','').toString();
    return (delimiter) ? 'delimiter=' + delimiter : '';
  }.property('delimiter'),

  markerParameter: function(){
    return 'marker=' + this.getWithDefault('marker','').toString();
  }.property('marker'),

  maxKeysParameter: function(){
    return 'max-keys=' + this.getWithDefault('maxKeys','').toString();
  }.property('maxKeys'),

  prefixParameter: function(){
    return 'prefix=' + this.getWithDefault('prefix','').toString();
  }.property('prefix'),

  queryProtocol: function() {
    return this.get('queryUseSSL') ? 'https://' : 'http://';
  }.property('queryUseSSL'),

  queryBaseUrl: function(){
    return this.get('queryProtocol') + this.get('endpoint') + '/' + this.get('bucket')
  }.property('queryProtocol', 'endpoint', 'bucket'),

  objectProtocol: function() {
    return this.get('objectUseSSL') ? 'https://' : 'http://';
  }.property('objectUseSSL'),

  objectBaseUrl: function(){
    return this.get('objectProtocol') + this.get('bucket');
  }.property('objectProtocol', 'bucket'),

  queryParams: function(){
    return this.get('delimiterParameter')  + '&' +
      this.get('markerParameter')     + '&' +
      this.get('maxKeysParameter')    + '&' +
      this.get('prefixParameter');
  }.property('delimiterParameter','markerParameter','maxKeysParameter','prefixParameter'),

  queryUrl: function(){
    return this.get('queryBaseUrl') + '?' + this.get('queryParams');
  }.property('queryBaseUrl','queryParams'),

  filesPresent: function(){
    return this.get('files').length;
  }.property('files.@each'),

  filterFiles: function(filter, ignoreFiles){
    var files = this.get('files');
    var ignoreFiles = Ember.A(ignoreFiles);

    return files.filter(function(e) {
      var name = e.get('name');
      var ignored = ignoreFiles.any(function(f) { return name.indexOf(f) >= 0; });
      var selected = filter.any(function(f) { return name.match(f); });

      return !ignored && selected;
    });
  },

  load: function(){
    var self = this,
    baseUrl = this.get('objectBaseUrl');

    this.set('isLoading', true);
    Ember.$.get(this.get('queryUrl'), function(data){
      self.set('isLoading', false);
      self.set('response', data);

      var contents = data.getElementsByTagName('Contents'),
      length   = contents.length,
      files    = [];

      for(var i = 0; i < length; i++) {
        var size = contents[i].getElementsByTagName('Size')[0].firstChild.data,
        name = contents[i].getElementsByTagName('Key')[0].firstChild.data,
        lastModified = new Date(contents[i].getElementsByTagName('LastModified')[0].firstChild.data);

        files.push(
          App.S3File.create({
          name: name,
          size: size,
          lastModified: lastModified,
          relativePath: name,
          baseUrl: baseUrl
        })
        );
      }

      self.set('files', files.sort(function(a,b){
        return b.lastModified - a.lastModified;
      }));
    });
  }.observes('queryUrl').on('init')
});

App.S3File = Ember.Object.extend({
  scriptTag: function(){
    var escapedURL = Ember.Handlebars.Utils.escapeExpression(this.get('url'));

    return new Ember.Handlebars.SafeString('<script src="' + escapedURL + '"></script>').toString();
  }.property('url'),

  url: function(){
    return this.get('baseUrl') + '/' + this.get('relativePath');
  }.property('baseUrl', 'relativePath')
});

App.Project = Ember.Object.extend();

App.Project.reopenClass({
  FIXTURES:
    [ {
      projectName: 'Ember',
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      channel: "tagged"
    }, {
      projectName: 'Ember Data',
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      channel: "tagged"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      initialVersion: "1.12.0",
      initialReleaseDate: "2015-03-28",
      lastRelease: "1.12.0",
      futureVersion: "1.12.1",
      channel: "release",
      date: "2015-05-13",
      changelogPath: "CHANGELOG.md",
      enableTestURL: true,
      debugFileName: ".debug.js"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      lastRelease: "1.13.0-beta.2",
      futureVersion: "1.13.0-beta.3",
      finalVersion: '1.13.0',
      channel: "beta",
      cycleEstimatedFinishDate: '2015-06-12',
      date: "2015-05-25",
      nextDate: "2015-05-30",
      changelogPath: "CHANGELOG.md",
      enableTestURL: true,
      debugFileName: ".debug.js",
      ignoreFiles: ['ember.js']
    }, {
      projectName: "Ember Data",
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      lastRelease: "1.0.0-beta.18",
      futureVersion: "1.0.0-beta.19",
      channel: "beta",
      date: "2015-05-21",
      changelogPath: "CHANGELOG.md",
      debugFileName: ".js"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      channel: "canary",
      enableTestURL: true,
      debugFileName: ".debug.js",
      ignoreFiles: ['ember.js']
    }, {
      projectName: "Ember Data",
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      channel: "canary",
      debugFileName: ".js"
    }],

  all: function(channel){
    var projects;

    if (channel)
      projects = this.FIXTURES.filterBy('channel', channel);
    else
      projects = this.FIXTURES;

    return projects.map(function(obj) {
      return App.Project.create(obj);
    });
  },

  find: function(channel, name) {
    var allProjects = this.all(channel);

    if (!name)
      return allProjects;
    else
      return allProjects.filterBy('projectName', name);
  }
});

App.BuildCategoryMixin = Ember.Mixin.create({
  renderTemplate: function() {
    this.render('build-list');
  }
});

App.ApplicationController = Ember.Controller.extend({
  isIndexActive: function(){
    return this.isActiveChannel('index');
  }.property('currentRouteName'),

  isTaggedActive: function(){
    return this.isActiveChannel('tagged');
  }.property('currentRouteName'),

  isChannelsActive: function(){
    var self = this;
    return !['index','tagged'].some(function(name){ return name === self.get('currentRouteName'); })
  }.property('currentRouteName'),

  isReleaseActive: function(){
    return this.isActiveChannel('release');
  }.property('currentRouteName'),

  isBetaActive: function(){
    return this.isActiveChannel('beta');
  }.property('currentRouteName'),

  isCanaryActive: function(){
    return this.isActiveChannel('canary');
  }.property('currentRouteName'),

  isActiveChannel: function(channel){
    return this.get('currentRouteName').indexOf(channel) !== -1;
  }
});

App.ProjectsMixin = Ember.Mixin.create({
  projects: function(){
    var projects = App.Project.find(this.get('channel')),
        bucket   = this.get('model'),
        self = this;

    projects.forEach(function(project){
      if (project.channel === 'beta'){
        project.isEmberBeta = project.projectName === 'Ember';

        [1,2,3,4,5].forEach(function(increment){
          var versionParts = project.lastRelease.split('.');
          var currentBetaNumber = parseInt(versionParts[versionParts.length - 1], 10);
          project['beta' + increment + 'Completed'] = increment <= currentBetaNumber;
          project['isBeta' + increment] = increment === currentBetaNumber;
        });

        var release = App.Project.find('release', project.projectName)[0];

        // no releases exist for ember-data (yet)
        if (release) {
          project.lastStableVersion = release.initialVersion;
          project.lastStableDate = release.initialReleaseDate;
        }
      }

      project.files = bucket.filterFiles(project.projectFilter, project.ignoreFiles);
      project.description = self.description(project);
      project.lastReleaseDebugUrl = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, project.debugFileName);
      project.lastReleaseProdUrl  = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '.prod.js');
      project.lastReleaseMinUrl   = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '.min.js');

      if (project.enableTestURL) {
        project.lastReleaseTestUrl  = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '-tests-index.html');
      }

      if (project.channel === 'canary') {
        project.lastRelease = 'latest';
      } else if (project.changelog !== 'false') {
        project.lastReleaseChangelogUrl   = 'https://github.com/' + project.projectRepo + '/blob/v' + project.lastRelease + '/' + project.changelogPath;
      }
    });

    return projects;
  }.property('channel', 'model'),

  description: function(project){
    var lastRelease = project.lastRelease,
        futureVersion = project.futureVersion,
        value;

    if (this.get('channel') === 'tagged') {
      value = '';
    } else if (lastRelease) {
      value = 'The builds listed below are incremental improvements made since ' + lastRelease + ' and may become ' + futureVersion + '.'
    } else if (futureVersion) {
      value = 'The builds listed below are not based on a tagged release. Upon the next release cycle they will become ' + futureVersion + '.';
    } else {
      value = 'The builds listed below are based on the most recent development.';
    }

    return new Ember.Handlebars.SafeString(value);
  },

  lastReleaseUrl: function(project, channel, lastRelease, extension){
    if (channel === 'canary')
      return 'http://builds.emberjs.com/canary/' + project + extension;
    else
      return 'http://builds.emberjs.com/tags/v' + lastRelease + '/' + project + extension;
  }

});

App.CanaryRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Canary Builds', prefix: 'canary/' });
  }
});

App.CanaryController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'canary'
});

App.BetaRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Beta Builds', prefix: 'beta/'});
  }
});

App.BetaController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'beta'
});

App.ReleaseRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Release Builds', prefix: 'release/'});
  }
});

App.ReleaseController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'release'
});

App.TaggedRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    var bucket = App.S3Bucket.create({
      title: 'Tagged Release Builds',
      prefix: 'tags/',
      delimiter: '',
    });
    return bucket;
  }
});

App.TaggedController = Ember.Controller.extend(App.ProjectsMixin, {
  channel: 'tagged'
});
/*
 * Handlebars Helpers
 */
Ember.Handlebars.helper('format-date-time', function(date, format, options) {
  if (!options) {
    options = format;
    format = null;
  }

  if (format){
    return moment(date).format(format);
  } else {
    return moment(date).fromNow();
  }
});
<commit_msg>Update 1.12.1 on /builds.<commit_after>//= require_tree ./templates

var App = Ember.Application.create({
  rootElement: '#builds-application'
});

App.Router.map(function() {
  this.route('release');
  this.route('beta');
  this.route('canary');
  this.route('tagged');
});

App.Router.reopen({
  notifyGoogleAnalytics: function() {
    var url = this.get('url');

    // Add a slash if neccesary
    if (!/^\//.test(url)){ url = '/' + url; }

    _gaq.push(['_trackPageview', '/builds' + url]);
  }.on('didTransition')
});

App.CopyClipboardComponent = Ember.Component.extend({
  tagName: 'span',
  hasFlash: ZeroClipboard.detectFlashSupport(),

  didInsertElement: function () {
    var clip = new ZeroClipboard(this.$('button'), {
      // This would normally be a relative path
      moviePath: "/images/ZeroClipboard.swf",

      trustedDomains: ["*"],
      allowScriptAccess: "always"
    });

    clip.on('mousedown', function(client, e) {
      Em.run.later(this, function() {
        $(this).removeClass('loading');
        $(this).removeAttr('disabled');
      }, 1000);
      Em.run.next(this, function() {
        $(this).addClass('loading');
        $(this).attr('disabled', 'disabled');
      });
    });

    this.$('input').on('click', function() {
      $(this).select();
    });
  }
});

App.S3Bucket = Ember.Object.extend({
  files: [],
  isLoading: false,

  // Setup these as default values.
  // They can be overridden on create.
  queryUseSSL: true,
  objectUseSSL: false,
  delimiter: '/',
  bucket: 'builds.emberjs.com',
  endpoint: 's3.amazonaws.com',

  delimiterParameter: function(){
    var delimiter = this.getWithDefault('delimiter','').toString();
    return (delimiter) ? 'delimiter=' + delimiter : '';
  }.property('delimiter'),

  markerParameter: function(){
    return 'marker=' + this.getWithDefault('marker','').toString();
  }.property('marker'),

  maxKeysParameter: function(){
    return 'max-keys=' + this.getWithDefault('maxKeys','').toString();
  }.property('maxKeys'),

  prefixParameter: function(){
    return 'prefix=' + this.getWithDefault('prefix','').toString();
  }.property('prefix'),

  queryProtocol: function() {
    return this.get('queryUseSSL') ? 'https://' : 'http://';
  }.property('queryUseSSL'),

  queryBaseUrl: function(){
    return this.get('queryProtocol') + this.get('endpoint') + '/' + this.get('bucket')
  }.property('queryProtocol', 'endpoint', 'bucket'),

  objectProtocol: function() {
    return this.get('objectUseSSL') ? 'https://' : 'http://';
  }.property('objectUseSSL'),

  objectBaseUrl: function(){
    return this.get('objectProtocol') + this.get('bucket');
  }.property('objectProtocol', 'bucket'),

  queryParams: function(){
    return this.get('delimiterParameter')  + '&' +
      this.get('markerParameter')     + '&' +
      this.get('maxKeysParameter')    + '&' +
      this.get('prefixParameter');
  }.property('delimiterParameter','markerParameter','maxKeysParameter','prefixParameter'),

  queryUrl: function(){
    return this.get('queryBaseUrl') + '?' + this.get('queryParams');
  }.property('queryBaseUrl','queryParams'),

  filesPresent: function(){
    return this.get('files').length;
  }.property('files.@each'),

  filterFiles: function(filter, ignoreFiles){
    var files = this.get('files');
    var ignoreFiles = Ember.A(ignoreFiles);

    return files.filter(function(e) {
      var name = e.get('name');
      var ignored = ignoreFiles.any(function(f) { return name.indexOf(f) >= 0; });
      var selected = filter.any(function(f) { return name.match(f); });

      return !ignored && selected;
    });
  },

  load: function(){
    var self = this,
    baseUrl = this.get('objectBaseUrl');

    this.set('isLoading', true);
    Ember.$.get(this.get('queryUrl'), function(data){
      self.set('isLoading', false);
      self.set('response', data);

      var contents = data.getElementsByTagName('Contents'),
      length   = contents.length,
      files    = [];

      for(var i = 0; i < length; i++) {
        var size = contents[i].getElementsByTagName('Size')[0].firstChild.data,
        name = contents[i].getElementsByTagName('Key')[0].firstChild.data,
        lastModified = new Date(contents[i].getElementsByTagName('LastModified')[0].firstChild.data);

        files.push(
          App.S3File.create({
          name: name,
          size: size,
          lastModified: lastModified,
          relativePath: name,
          baseUrl: baseUrl
        })
        );
      }

      self.set('files', files.sort(function(a,b){
        return b.lastModified - a.lastModified;
      }));
    });
  }.observes('queryUrl').on('init')
});

App.S3File = Ember.Object.extend({
  scriptTag: function(){
    var escapedURL = Ember.Handlebars.Utils.escapeExpression(this.get('url'));

    return new Ember.Handlebars.SafeString('<script src="' + escapedURL + '"></script>').toString();
  }.property('url'),

  url: function(){
    return this.get('baseUrl') + '/' + this.get('relativePath');
  }.property('baseUrl', 'relativePath')
});

App.Project = Ember.Object.extend();

App.Project.reopenClass({
  FIXTURES:
    [ {
      projectName: 'Ember',
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      channel: "tagged"
    }, {
      projectName: 'Ember Data',
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      channel: "tagged"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      initialVersion: "1.12.0",
      initialReleaseDate: "2015-03-28",
      lastRelease: "1.12.1",
      futureVersion: "1.12.2",
      channel: "release",
      date: "2015-05-31",
      changelogPath: "CHANGELOG.md",
      enableTestURL: true,
      debugFileName: ".debug.js"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      lastRelease: "1.13.0-beta.2",
      futureVersion: "1.13.0-beta.3",
      finalVersion: '1.13.0',
      channel: "beta",
      cycleEstimatedFinishDate: '2015-06-12',
      date: "2015-05-25",
      nextDate: "2015-05-30",
      changelogPath: "CHANGELOG.md",
      enableTestURL: true,
      debugFileName: ".debug.js",
      ignoreFiles: ['ember.js']
    }, {
      projectName: "Ember Data",
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      lastRelease: "1.0.0-beta.18",
      futureVersion: "1.0.0-beta.19",
      channel: "beta",
      date: "2015-05-21",
      changelogPath: "CHANGELOG.md",
      debugFileName: ".js"
    }, {
      projectName: "Ember",
      baseFileName: 'ember',
      projectFilter: [ /ember\./, /ember-template-compiler/ ],
      projectRepo: 'emberjs/ember.js',
      channel: "canary",
      enableTestURL: true,
      debugFileName: ".debug.js",
      ignoreFiles: ['ember.js']
    }, {
      projectName: "Ember Data",
      baseFileName: 'ember-data',
      projectFilter: [ /ember-data\./ ],
      projectRepo: 'emberjs/data',
      channel: "canary",
      debugFileName: ".js"
    }],

  all: function(channel){
    var projects;

    if (channel)
      projects = this.FIXTURES.filterBy('channel', channel);
    else
      projects = this.FIXTURES;

    return projects.map(function(obj) {
      return App.Project.create(obj);
    });
  },

  find: function(channel, name) {
    var allProjects = this.all(channel);

    if (!name)
      return allProjects;
    else
      return allProjects.filterBy('projectName', name);
  }
});

App.BuildCategoryMixin = Ember.Mixin.create({
  renderTemplate: function() {
    this.render('build-list');
  }
});

App.ApplicationController = Ember.Controller.extend({
  isIndexActive: function(){
    return this.isActiveChannel('index');
  }.property('currentRouteName'),

  isTaggedActive: function(){
    return this.isActiveChannel('tagged');
  }.property('currentRouteName'),

  isChannelsActive: function(){
    var self = this;
    return !['index','tagged'].some(function(name){ return name === self.get('currentRouteName'); })
  }.property('currentRouteName'),

  isReleaseActive: function(){
    return this.isActiveChannel('release');
  }.property('currentRouteName'),

  isBetaActive: function(){
    return this.isActiveChannel('beta');
  }.property('currentRouteName'),

  isCanaryActive: function(){
    return this.isActiveChannel('canary');
  }.property('currentRouteName'),

  isActiveChannel: function(channel){
    return this.get('currentRouteName').indexOf(channel) !== -1;
  }
});

App.ProjectsMixin = Ember.Mixin.create({
  projects: function(){
    var projects = App.Project.find(this.get('channel')),
        bucket   = this.get('model'),
        self = this;

    projects.forEach(function(project){
      if (project.channel === 'beta'){
        project.isEmberBeta = project.projectName === 'Ember';

        [1,2,3,4,5].forEach(function(increment){
          var versionParts = project.lastRelease.split('.');
          var currentBetaNumber = parseInt(versionParts[versionParts.length - 1], 10);
          project['beta' + increment + 'Completed'] = increment <= currentBetaNumber;
          project['isBeta' + increment] = increment === currentBetaNumber;
        });

        var release = App.Project.find('release', project.projectName)[0];

        // no releases exist for ember-data (yet)
        if (release) {
          project.lastStableVersion = release.initialVersion;
          project.lastStableDate = release.initialReleaseDate;
        }
      }

      project.files = bucket.filterFiles(project.projectFilter, project.ignoreFiles);
      project.description = self.description(project);
      project.lastReleaseDebugUrl = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, project.debugFileName);
      project.lastReleaseProdUrl  = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '.prod.js');
      project.lastReleaseMinUrl   = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '.min.js');

      if (project.enableTestURL) {
        project.lastReleaseTestUrl  = self.lastReleaseUrl(project.baseFileName, project.channel, project.lastRelease, '-tests-index.html');
      }

      if (project.channel === 'canary') {
        project.lastRelease = 'latest';
      } else if (project.changelog !== 'false') {
        project.lastReleaseChangelogUrl   = 'https://github.com/' + project.projectRepo + '/blob/v' + project.lastRelease + '/' + project.changelogPath;
      }
    });

    return projects;
  }.property('channel', 'model'),

  description: function(project){
    var lastRelease = project.lastRelease,
        futureVersion = project.futureVersion,
        value;

    if (this.get('channel') === 'tagged') {
      value = '';
    } else if (lastRelease) {
      value = 'The builds listed below are incremental improvements made since ' + lastRelease + ' and may become ' + futureVersion + '.'
    } else if (futureVersion) {
      value = 'The builds listed below are not based on a tagged release. Upon the next release cycle they will become ' + futureVersion + '.';
    } else {
      value = 'The builds listed below are based on the most recent development.';
    }

    return new Ember.Handlebars.SafeString(value);
  },

  lastReleaseUrl: function(project, channel, lastRelease, extension){
    if (channel === 'canary')
      return 'http://builds.emberjs.com/canary/' + project + extension;
    else
      return 'http://builds.emberjs.com/tags/v' + lastRelease + '/' + project + extension;
  }

});

App.CanaryRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Canary Builds', prefix: 'canary/' });
  }
});

App.CanaryController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'canary'
});

App.BetaRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Beta Builds', prefix: 'beta/'});
  }
});

App.BetaController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'beta'
});

App.ReleaseRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    return App.S3Bucket.create({title: 'Release Builds', prefix: 'release/'});
  }
});

App.ReleaseController = Ember.Controller.extend(App.ProjectsMixin, {
  templateName: 'buildList',
  channel: 'release'
});

App.TaggedRoute = Ember.Route.extend(App.BuildCategoryMixin, {
  model: function() {
    var bucket = App.S3Bucket.create({
      title: 'Tagged Release Builds',
      prefix: 'tags/',
      delimiter: '',
    });
    return bucket;
  }
});

App.TaggedController = Ember.Controller.extend(App.ProjectsMixin, {
  channel: 'tagged'
});
/*
 * Handlebars Helpers
 */
Ember.Handlebars.helper('format-date-time', function(date, format, options) {
  if (!options) {
    options = format;
    format = null;
  }

  if (format){
    return moment(date).format(format);
  } else {
    return moment(date).fromNow();
  }
});
<|endoftext|>


<commit_before>/*  VCO.TimelineConfig
    separate the configuration from the display (VCO.Timeline)
    to make testing easier
================================================== */
VCO.TimelineConfig = VCO.Class.extend({
    VALID_PROPERTIES: ['scale', 'slides'], // we'll only pull things in from this

    initialize: function (data, callback) {
    // Initialize the data
        if (typeof data === 'string') {
            var self = this;

            VCO.ajax({
                type: 'GET',
                url: data,
                dataType: 'json', //json data type
                success: function(d){
                    if (d && d.timeline) {
                        self._importProperties(d.timeline);
                    } else {
                        throw("data must have a timeline property")
                    }
                    self._cleanData();
                    if (callback) {
                        callback(self);
                    }
                },
                error:function(xhr, type){
                    trace(xhr);
                    trace(type);
                    throw("Configuration could not be loaded: " + type);
                }
            });
        } else if (typeof data === 'object') {
            if (data.timeline) {
                this._importProperties(data.timeline);
                this._cleanData();
            } else {
                throw("data must have a timeline property")
            }
            if (callback) {
                callback(this);
            }
        } else {
            throw("Invalid Argument");
        }
    },

    _cleanData: function() {
        this._makeUniqueIdentifiers(this.slides);
        this._processDates(this.slides);
        VCO.DateUtil.sortByDate(this.slides);
    },

    _importProperties: function(d) {
        for (var i = 0; i < this.VALID_PROPERTIES.length; i++) {
            k = this.VALID_PROPERTIES[i];
            this[k] = d[k];
        }
    },

    _makeUniqueIdentifiers: function(array) {
        var used = []
        for (var i = 0; i < array.length; i++) {
            if (array[i].uniqueid && array[i].uniqueid.replace(/\s+/,'').length > 0) {
                array[i].uniqueid = VCO.Util.slugify(array[i].uniqueid); // enforce valid
                if (used.indexOf(array[i].uniqueid) != -1) {
                    array[i].uniqueid = '';
                } else {
                    used.push(array[i].uniqueid);
                }
            }
        };
        if (used.length != array.length) {
            for (var i = 0; i < array.length; i++) {
                if (!array[i].uniqueid) {
                    var slug = (array[i].text) ? VCO.Util.slugify(array[i].text.headline) : null;
                    if (!slug) {
                        slug = VCO.Util.unique_ID(6);
                    }
                    if (used.indexOf(slug) != -1) {
                        slug = slug + '-' + i;
                    }
                    used.push(slug);
                    array[i].uniqueid = slug;
                }
            }
        }
    },

    _processDates: function(array) {
        var dateCls = null;

        if(this.scale == 'javascript') {
            dateCls = VCO.Date;
            trace('using VCO.Date');
        } else if(this.scale == 'cosmological') {
            dateCls = VCO.BigDate;
            trace('using VCO.BigDate');
        } else {
            throw ("Don't know how to process dates on scale "+this.scale);
        }

        for (var i = 0; i < array.length; i++) {
            if (typeof(array[i].start_date) == 'undefined') {
                throw("item " + i + " is missing a start_date");
            }

            array[i].start_date = new dateCls(array[i].start_date);
            if (typeof(array[i].end_date) != 'undefined') {
                array[i].end_date = new dateCls(array[i].end_date);
            }
        }
    }
});
<commit_msg>Determine scale dynamically if not set in JSON.<commit_after>/*  VCO.TimelineConfig
    separate the configuration from the display (VCO.Timeline)
    to make testing easier
================================================== */
VCO.TimelineConfig = VCO.Class.extend({
    VALID_PROPERTIES: ['scale', 'slides'], // we'll only pull things in from this

    initialize: function (data, callback) {
    // Initialize the data
        if (typeof data === 'string') {
            var self = this;

            VCO.ajax({
                type: 'GET',
                url: data,
                dataType: 'json', //json data type
                success: function(d){
                    if (d && d.timeline) {
                        self._importProperties(d.timeline);
                    } else {
                        throw("data must have a timeline property")
                    }
                    self._cleanData();
                    if (callback) {
                        callback(self);
                    }
                },
                error:function(xhr, type){
                    trace(xhr);
                    trace(type);
                    throw("Configuration could not be loaded: " + type);
                }
            });
        } else if (typeof data === 'object') {
            if (data.timeline) {
                this._importProperties(data.timeline);
                this._cleanData();
            } else {
                throw("data must have a timeline property")
            }
            if (callback) {
                callback(this);
            }
        } else {
            throw("Invalid Argument");
        }
    },

    _cleanData: function() {
        this._makeUniqueIdentifiers(this.slides);
        this._processDates(this.slides);
        VCO.DateUtil.sortByDate(this.slides);
    },

    _importProperties: function(d) {
        for (var i = 0; i < this.VALID_PROPERTIES.length; i++) {
            k = this.VALID_PROPERTIES[i];
            this[k] = d[k];
        }
    },

    _makeUniqueIdentifiers: function(array) {
        var used = []
        for (var i = 0; i < array.length; i++) {
            if (array[i].uniqueid && array[i].uniqueid.replace(/\s+/,'').length > 0) {
                array[i].uniqueid = VCO.Util.slugify(array[i].uniqueid); // enforce valid
                if (used.indexOf(array[i].uniqueid) != -1) {
                    array[i].uniqueid = '';
                } else {
                    used.push(array[i].uniqueid);
                }
            }
        };
        if (used.length != array.length) {
            for (var i = 0; i < array.length; i++) {
                if (!array[i].uniqueid) {
                    var slug = (array[i].text) ? VCO.Util.slugify(array[i].text.headline) : null;
                    if (!slug) {
                        slug = VCO.Util.unique_ID(6);
                    }
                    if (used.indexOf(slug) != -1) {
                        slug = slug + '-' + i;
                    }
                    used.push(slug);
                    array[i].uniqueid = slug;
                }
            }
        }
    },

    _processDates: function(array) {
        var dateCls = null;

        if(!this.scale) {
            trace("Determining scale dynamically");

            this.scale = "javascript"; // default

            for (var i = 0; i < array.length; i++) {
                if (typeof(array[i].start_date) == 'undefined') {
                    throw("item " + i + " is missing a start_date");
                }

                var d = new VCO.BigDate(array[i].start_date);
                var year = d.data.date_obj.year;
                if(year < -271820 || year >  275759) {
                    this.scale = "cosmological";
                    break;
                }
            }
        }

        if(this.scale == 'javascript') {
            dateCls = VCO.Date;
            trace('using VCO.Date');
        } else if(this.scale == 'cosmological') {
            dateCls = VCO.BigDate;
            trace('using VCO.BigDate');
        } else {
            throw ("Don't know how to process dates on scale "+this.scale);
        }

        for (var i = 0; i < array.length; i++) {
            if (typeof(array[i].start_date) == 'undefined') {
                throw("item " + i + " is missing a start_date");
            }

            array[i].start_date = new dateCls(array[i].start_date);
            if (typeof(array[i].end_date) != 'undefined') {
                array[i].end_date = new dateCls(array[i].end_date);
            }
        }
    }
});
<|endoftext|>


<commit_before>var appConfig = require('./passport-config'),
    passport = require('passport'),
    passportOauth = require('passport-oauth'),
    OAuthStrategy = passportOauth.OAuthStrategy,
    OAuth2Strategy = passportOauth.OAuth2Strategy,
    request = require('request');

var passportPlugin = {};

var logins = [];
var users = [];

var hasApi = function(apiName) {
  return appConfig[apiName] != undefined;
};
passportPlugin.hasApi = hasApi;

var findUser = function(apiName,req) {

  if(req.session.user == undefined
     || logins[req.session.user.id] == undefined) {
    return undefined;
  }

  return users[logins[req.session.user.id][apiName]];
};
passportPlugin.findUser = findUser;

var init = function(app) {

  app.use(passport.initialize());
  app.use(passport.session());

  // store user in persistent storage for passport
  passport.serializeUser(function(user, done) {
    //console.log('serialize user ' + user._id);
    users[user._id] = user;

    if(logins[user._login] == undefined) {
      logins[user._login] = [];
      logins[user._login][user._api] = user._id;
    }
    else {
      logins[user._login][user._api] = user._id;
    }

    done(null, user._id);
});

  // retrieve user from persistent store for passport
  passport.deserializeUser(function(id, done) {
    //console.log('deserialize user ' + id);
    var user = users[id];
    done(null, user);
  });

  // load all app configs
  for(var apiName in appConfig) {
    console.log('register = ' + apiName);
    register(apiName);
  }

};
passportPlugin.init = init;

var register = function(apiName) {

   var strategyCallback = function(req, token, tokenSecret, profile, done) {
    var fakeUser = { 'token' : token,
                     'tokenSecret' : tokenSecret };
    var options =  { 'method' : 'GET',
                     'uri' : appConfig[apiName].defaultLoadUrl };
    var callback = function(user, data) {
      var fullUser = null;

      //var profileUser = appConfig[apiName].userElementName?data[appConfig[apiName].userElementName]:data;
      var profileUserId = null;
      if(data) {
        var userIdPath = appConfig[apiName].userIdPath.split('.');
        var profileUser = data;
        for(var i=0;i<userIdPath.length;i++) {
          if(i==userIdPath.length-1) {
            profileUserId = profileUser[userIdPath[i]];
          }
          else {
            profileUser = profileUser[userIdPath[i]];
          }
        }
      }

      if(profileUserId) {
        fullUser = {};
        fullUser.profile = data;
        fullUser._id = apiName + "~" + profileUserId;
        fullUser.token = fakeUser.token;
        fullUser.tokenSecret = fakeUser.tokenSecret;
        fullUser._login = req.session.user.id;
        fullUser._api = apiName;
      }
      done(null, fullUser);
    };
    handleRequest(apiName,fakeUser,options,callback);
  };

  if(appConfig[apiName].type == 'oauth-1.0') {
    // setup oauth1 through passport
    passport.use(apiName,
      new OAuthStrategy({
          userAuthorizationURL: appConfig[apiName].authorizationUrl,
          requestTokenURL: appConfig[apiName].requestTokenUrl,
          accessTokenURL: appConfig[apiName].accessTokenUrl,
          consumerKey: appConfig[apiName].clientId,
          consumerSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else if(appConfig[apiName].type == 'oauth-2.0') {
    // setup oauth2 through passport
    passport.use(apiName,
      new OAuth2Strategy({
          authorizationURL: appConfig[apiName].authorizationUrl,
          tokenURL: appConfig[apiName].accessTokenUrl,
          clientID: appConfig[apiName].clientId,
          clientSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else {
    console.log(apiName + " has unknown auth type of " + appConfig[apiName].type);
  }
};
passportPlugin.register = register;

// simplified request handling
var handleRequest = function(apiName, user, options, callback) {
  console.log('handling request to ' + options.uri);

  var _oauth = undefined;
  if(appConfig[apiName].type == 'oauth-1.0') {
    _oauth =passport._strategy(apiName)._oauth;
  }

  if(appConfig[apiName].type == 'oauth-1.0') {
    _oauth =passport._strategy(apiName)._oauth2;
  }

  _oauth.get(appConfig[apiName].baseUrl+options.uri, user.token, user.tokenSecret, function (error, body, response) {
    if(error) {
      console.log('handle request error ' + JSON.stringify(error));
      callback(user,null);
      return;
    }

    if (response.statusCode === 200) {
      //console.log("RESULT=" + body);
      callback(user,JSON.parse(body));
    } else {
      console.log('handle request error ' + response.statusCode);
      callback(user,response.statusCode);
    }
  });
};
passportPlugin.handleRequest = handleRequest;

var auth = function(apiName,streams) {
  //console.log('auth attempt');
  passport.authenticate(apiName)(streams.req,streams.res,streams.next);
}
passportPlugin.auth = auth;

var authCallback = function(apiName,urls,streams) {
  //console.log('authCallback attempt');
  passport.authenticate(apiName,
                          {
                            'successRedirect' : urls.successUrl,
                            'failureRedirect' : urls.failureUrl
                          }
                       )(streams.req,streams.res,streams.next);
}
passportPlugin.authCallback = authCallback;

module.exports = passportPlugin;
<commit_msg>fixed oauth2 request handling again<commit_after>var appConfig = require('./passport-config'),
    passport = require('passport'),
    passportOauth = require('passport-oauth'),
    OAuthStrategy = passportOauth.OAuthStrategy,
    OAuth2Strategy = passportOauth.OAuth2Strategy,
    request = require('request');

var passportPlugin = {};

var logins = [];
var users = [];

var hasApi = function(apiName) {
  return appConfig[apiName] != undefined;
};
passportPlugin.hasApi = hasApi;

var findUser = function(apiName,req) {

  if(req.session.user == undefined
     || logins[req.session.user.id] == undefined) {
    return undefined;
  }

  return users[logins[req.session.user.id][apiName]];
};
passportPlugin.findUser = findUser;

var init = function(app) {

  app.use(passport.initialize());
  app.use(passport.session());

  // store user in persistent storage for passport
  passport.serializeUser(function(user, done) {
    //console.log('serialize user ' + user._id);
    users[user._id] = user;

    if(logins[user._login] == undefined) {
      logins[user._login] = [];
      logins[user._login][user._api] = user._id;
    }
    else {
      logins[user._login][user._api] = user._id;
    }

    done(null, user._id);
});

  // retrieve user from persistent store for passport
  passport.deserializeUser(function(id, done) {
    //console.log('deserialize user ' + id);
    var user = users[id];
    done(null, user);
  });

  // load all app configs
  for(var apiName in appConfig) {
    console.log('register = ' + apiName);
    register(apiName);
  }

};
passportPlugin.init = init;

var register = function(apiName) {

   var strategyCallback = function(req, token, tokenSecret, profile, done) {
    var fakeUser = { 'token' : token,
                     'tokenSecret' : tokenSecret };
    var options =  { 'method' : 'GET',
                     'uri' : appConfig[apiName].defaultLoadUrl };
    var callback = function(user, data) {
      var fullUser = null;

      //var profileUser = appConfig[apiName].userElementName?data[appConfig[apiName].userElementName]:data;
      var profileUserId = null;
      if(data) {
        var userIdPath = appConfig[apiName].userIdPath.split('.');
        var profileUser = data;
        for(var i=0;i<userIdPath.length;i++) {
          if(i==userIdPath.length-1) {
            profileUserId = profileUser[userIdPath[i]];
          }
          else {
            profileUser = profileUser[userIdPath[i]];
          }
        }
      }

      if(profileUserId) {
        fullUser = {};
        fullUser.profile = data;
        fullUser._id = apiName + "~" + profileUserId;
        fullUser.token = fakeUser.token;
        fullUser.tokenSecret = fakeUser.tokenSecret;
        fullUser._login = req.session.user.id;
        fullUser._api = apiName;
      }
      done(null, fullUser);
    };
    handleRequest(apiName,fakeUser,options,callback);
  };

  if(appConfig[apiName].type == 'oauth-1.0') {
    // setup oauth1 through passport
    passport.use(apiName,
      new OAuthStrategy({
          userAuthorizationURL: appConfig[apiName].authorizationUrl,
          requestTokenURL: appConfig[apiName].requestTokenUrl,
          accessTokenURL: appConfig[apiName].accessTokenUrl,
          consumerKey: appConfig[apiName].clientId,
          consumerSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else if(appConfig[apiName].type == 'oauth-2.0') {
    // setup oauth2 through passport
    passport.use(apiName,
      new OAuth2Strategy({
          authorizationURL: appConfig[apiName].authorizationUrl,
          tokenURL: appConfig[apiName].accessTokenUrl,
          clientID: appConfig[apiName].clientId,
          clientSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else {
    console.log(apiName + " has unknown auth type of " + appConfig[apiName].type);
  }
};
passportPlugin.register = register;

// simplified request handling
var handleRequest = function(apiName, user, options, callback) {
  console.log('handling request to ' + options.uri);

  var _oauth = undefined;
  if(appConfig[apiName].type == 'oauth-1.0') {
    _oauth =passport._strategy(apiName)._oauth;
  }
  else if(appConfig[apiName].type == 'oauth-2.0') {
    _oauth =passport._strategy(apiName)._oauth2;
  }
  else {
    console.log('No request handling for ' + appConfig[apiName].type]);
    return;
  }

  _oauth.get(appConfig[apiName].baseUrl+options.uri, user.token, user.tokenSecret, function (error, body, response) {
    if(error) {
      console.log('handle request error ' + JSON.stringify(error));
      callback(user,null);
      return;
    }

    if (response.statusCode === 200) {
      //console.log("RESULT=" + body);
      callback(user,JSON.parse(body));
    } else {
      console.log('handle request error ' + response.statusCode);
      callback(user,response.statusCode);
    }
  });
};
passportPlugin.handleRequest = handleRequest;

var auth = function(apiName,streams) {
  //console.log('auth attempt');
  passport.authenticate(apiName)(streams.req,streams.res,streams.next);
}
passportPlugin.auth = auth;

var authCallback = function(apiName,urls,streams) {
  //console.log('authCallback attempt');
  passport.authenticate(apiName,
                          {
                            'successRedirect' : urls.successUrl,
                            'failureRedirect' : urls.failureUrl
                          }
                       )(streams.req,streams.res,streams.next);
}
passportPlugin.authCallback = authCallback;

module.exports = passportPlugin;
<|endoftext|>


<commit_before>function testLegal(source, target) {
    var move = game.move({
        from: source,
        to: target,
        promotion: 'q'
    });
    if (move === null) return false;
    else {
        game.undo();
        return true;
    }
    return undefined;
}

NLP = function() {
    var env, currentState;
    var dict = {};
    dict['knight'] = 'n';
    dict['king'] = 'k';
    dict['rook'] = 'r';
    dict['pawn'] = 'p';
    dict['queen'] = 'q';
    dict['bishop'] = 'b';
    var dictR = {};
    dictR['b'] = 'bishop';
    dictR['n'] = 'knight';
    dictR['k'] = 'king';
    dictR['r'] = 'rook';
    dictR['p'] = 'pawn';
    dictR['q'] = 'queen';
    var Sentence = function(content, owner, pieces, preps, dets, controlkey) {
        this.content = content;
        this.owner = owner;
        this.pieces = pieces;
        this.preps = preps;
        this.dets = dets;
        this.controlkey = controlkey;
        if (owner === 'usr') {
            if (controlkey !== null) {
                this.intent = 'control';
            } else if (dets !== null) {
                this.intent = 'inquiry';
            } else if ($.inArray('to', preps) > -1 || $.inArray('take', preps) > -1) {
                this.intent = 'move';
            } else if ($.inArray('at', preps) > -1 || $.inArray('on', preps) > -1 || $.inArray('in', preps) > -1) {
                this.intent = 'inquiry';
            } else this.intent = 'other';
        }
        return {
            content: this.content,
            owner: this.owner,
            pieces: this.pieces,
            preps: this.preps,
            dets: this.dets,
            controlkey: this.controlkey,
            intent: this.intent
        }
    }
    var sysLog = function(outstr) {
        env.push(new Sentence(outstr, 'sys'), 'reply');
        return outstr;
    };
    //parsing functions
    var getPieces = function(content) {
        return content.match(/([a-h][1-8])|knight|bishop|queen|king|pawn|rook/g);
    };
    var getPreps = function(content) {
        return content.match(/to|take|on|at|in/g);
    };
    var getDets = function(content) {
        return content.match(/what|who|which|how|when|where|can|could|may/g);
    };
    var getControlKey = function(content) {
        return content.match(/reset|restart|undo|surrender|repeat/g);
    }

    //the environment object, main data structure
    var ENV = function() {
        dialogs = [];
        var getCurDialog = function() {
            return dialogs[dialogs.length - 1];
        };
        var getCurSentence = function() {
            curdialog = getCurDialog();
            return curdialog[curdialog.length - 1];
        };
        var getLastSysSentence = function() {
            for (var k = dialogs.length - 1; k >= 0; k--) {
                var lastDialog = dialogs[k];
                for (var i = lastDialog.length - 1; i >= 0; i--) {
                    if (lastDialog[i].owner === 'sys') {
                        return lastDialog[i];
                    }
                }
            }
        };
        var getLastUsrSentence = function(n) {
            if (n === undefined) {
                for (var k = dialogs.length - 1; k >= 0; k--) {
                    var lastDialog = dialogs[k];
                    for (var i = lastDialog.length - 1; i >= 0; i--) {
                        if (lastDialog[i].owner === 'usr') {
                            return lastDialog[i];
                        }
                    }
                }
            } else {
                for (var k = dialogs.length - 1; k >= 0; k--) {
                    var lastDialog = dialogs[k];
                    for (var i = lastDialog.length - 1; i >= 0; i--) {
                        if (lastDialog[i].owner === 'usr') {
                            n--;
                            if (n === 0) {
                                return lastDialog[i];
                            }
                        }
                    }
                }
            }
        };
        var push = function(sentence, state) {
            if (state === 'new') {
                //create new dialog
                var newSentences = [];
                newSentences.push(sentence);
                dialogs.push(newSentences);
            } else {
                //push to current dialog
                var lastDialog = dialogs.pop();
                lastDialog.push(sentence);
                dialogs.push(lastDialog);
            }
        }
        return {
            init: function() {
                var sentences = [];
                var sentence = new Sentence('Hello from the NLP system. Input your command please. The instructions are on the left.', 'sys');
                push(sentence, currentState);
                return getCurSentence();
            },
            getCurDialog: function() {
                return getCurDialog();
            },
            getCurSentence: function() {
                return getCurSentence();
            },
            push: function(sentence, state) {
                return push(sentence, state);
            },
            getLastSysSentence: function() {
                return getLastSysSentence();
            },
            getLastUsrSentence: function(n) {
                return getLastUsrSentence(n);
            }
        }
    };

    var parseToSentence = function(content) {
        var owner = 'usr';
        var pieces = getPieces(content);
        var preps = getPreps(content);
        var dets = getDets(content);
        var controlkey = getControlKey(content);
        var sentence = new Sentence(content, owner, pieces, preps, dets, controlkey);
        env.push(sentence, currentState);
        return sentence;
    };

    //format the input
    var beautify = function(content) {
        content = content.trim()
            .toLowerCase()
            .replace(/\s+/g, '');
        return content;
    };

    //chess/chessboard control
    var move = function(source, target) {
        var generateOptions = function(pieces) {
            var output = '';
            _.each(pieces, function(piece) {
                output += piece.loc + ' / ';
            });
            output = output.replace(/\/\s+$/g, '');
            output = output.replace(/\s+$/g, '');
            output += '?';
            return output;
        }
        if (source.search(/[a-h][1-8]/) > -1) {
            onDrop(source, target);
            myboard.position(game.fen());
            return sysLog('Moved from ' + source + ' to ' + target + '.');
        } else {
            var availablePieces = [];
            _.each(game.SQUARES, function(piece) {
                var tmp = game.get(piece);
                if (tmp !== null && tmp !== undefined) {
                    if (dictR[tmp.type] === source && game.turn() === tmp.color) {
                        if (testLegal(piece, target) === true) {
                            availablePieces.push({
                                loc: piece,
                                piece: tmp
                            });
                        }
                    }
                }
            });
            if (availablePieces.length === 1) {
                onDrop(availablePieces[0].loc, target);
                myboard.position(game.fen());
                return sysLog('Moved ' + dictR[availablePieces[0].piece.type] + ' at ' + availablePieces[0].loc + ' to ' + target + '.');
            } else if (availablePieces.length > 1) {
                currentState = 'moreValidMoves';
                return sysLog('More than one valid move, ' + dictR[availablePieces[0].piece.type] + ' at ' + generateOptions(availablePieces));
            }
        }
    };

    var dispatcher = function(sentence, state, env) {
        switch (state.content) {
            case 'new':
                switch (sentence.intent) {
                    case 'control':
                        if ($.inArray('repeat', sentence.controlkey) > -1) {
                            return env.getLastSysSentence().content;
                        }
                        break;
                    case 'inquiry':

                        break;
                    case 'move':
                        var pieces = sentence.pieces;
                        if (pieces.length === 2) {
                            return move(pieces[0], pieces[1]);
                        }
                        break;
                    case 'other':

                        break;
                    default:
                        console.log('bad switch intent');
                        return;
                }
                break;
            case 'moreValidMoves':
                var onlypiece = sentence.pieces[0];
                if (onlypiece !== null && onlypiece !== undefined) {
                    var target = env.getLastUsrSentence(2).pieces[1];
                    currentState = 'new';
                    return move(onlypiece, target);
                };
                break;
            default:
                return undefined;
        }
    }

    //public APIs
    return {
        init: function() {
            env = new ENV();
            currentState = 'new';
            return env.init();
        },
        input: function(content) {
            content = beautify(content);
            //content = errorCheck(content);
            var sentence = parseToSentence(content);
            if (sentence.intent !== null || sentence.intent !== undefined) {
                return dispatcher(sentence, {
                    content: currentState
                }, env);
            }
        },
        resetState: function() {
            if (currentState !== 'new') {
                currentState = 'new';
                return sysLog('New dialog begins.');
            }
            return undefined;
        }
    }
}
<commit_msg>inquiry done<commit_after>function testLegal(source, target) {
    var move = game.move({
        from: source,
        to: target,
        promotion: 'q'
    });
    if (move === null) return false;
    else {
        game.undo();
        return true;
    }
    return undefined;
}

NLP = function() {
    var env, currentState;
    var dict = {};
    dict['knight'] = 'n';
    dict['king'] = 'k';
    dict['rook'] = 'r';
    dict['pawn'] = 'p';
    dict['queen'] = 'q';
    dict['bishop'] = 'b';
    var dictR = {};
    dictR['b'] = 'bishop';
    dictR['n'] = 'knight';
    dictR['k'] = 'king';
    dictR['r'] = 'rook';
    dictR['p'] = 'pawn';
    dictR['q'] = 'queen';
    var Sentence = function(content, owner, pieces, preps, dets, controlkey) {
        this.content = content;
        this.owner = owner;
        this.pieces = pieces;
        this.preps = preps;
        this.dets = dets;
        this.controlkey = controlkey;
        if (owner === 'usr') {
            if (controlkey !== null) {
                this.intent = 'control';
            } else if (dets !== null) {
                this.intent = 'inquiry';
            } else if ($.inArray('to', preps) > -1 || $.inArray('take', preps) > -1) {
                this.intent = 'move';
            } else if ($.inArray('at', preps) > -1 || $.inArray('on', preps) > -1 || $.inArray('in', preps) > -1) {
                this.intent = 'inquiry';
            } else this.intent = 'other';
        }
        return {
            content: this.content,
            owner: this.owner,
            pieces: this.pieces,
            preps: this.preps,
            dets: this.dets,
            controlkey: this.controlkey,
            intent: this.intent
        }
    }
    var sysLog = function(outstr) {
        env.push(new Sentence(outstr, 'sys'), 'reply');
        return outstr;
    };
    //parsing functions
    var getPieces = function(content) {
        return content.match(/([a-h][1-8])|knight|bishop|queen|king|pawn|rook/g);
    };
    var getPreps = function(content) {
        return content.match(/to|take|on|at|in/g);
    };
    var getDets = function(content) {
        return content.match(/what|who|which|how|when|where|can|could|may/g);
    };
    var getControlKey = function(content) {
        return content.match(/reset|restart|undo|surrender|repeat/g);
    }

    //the environment object, main data structure
    var ENV = function() {
        dialogs = [];
        var getCurDialog = function() {
            return dialogs[dialogs.length - 1];
        };
        var getCurSentence = function() {
            curdialog = getCurDialog();
            return curdialog[curdialog.length - 1];
        };
        var getLastSysSentence = function() {
            for (var k = dialogs.length - 1; k >= 0; k--) {
                var lastDialog = dialogs[k];
                for (var i = lastDialog.length - 1; i >= 0; i--) {
                    if (lastDialog[i].owner === 'sys') {
                        return lastDialog[i];
                    }
                }
            }
        };
        var getLastUsrSentence = function(n) {
            if (n === undefined) {
                for (var k = dialogs.length - 1; k >= 0; k--) {
                    var lastDialog = dialogs[k];
                    for (var i = lastDialog.length - 1; i >= 0; i--) {
                        if (lastDialog[i].owner === 'usr') {
                            return lastDialog[i];
                        }
                    }
                }
            } else {
                for (var k = dialogs.length - 1; k >= 0; k--) {
                    var lastDialog = dialogs[k];
                    for (var i = lastDialog.length - 1; i >= 0; i--) {
                        if (lastDialog[i].owner === 'usr') {
                            n--;
                            if (n === 0) {
                                return lastDialog[i];
                            }
                        }
                    }
                }
            }
        };
        var push = function(sentence, state) {
            if (state === 'new') {
                //create new dialog
                var newSentences = [];
                newSentences.push(sentence);
                dialogs.push(newSentences);
            } else {
                //push to current dialog
                var lastDialog = dialogs.pop();
                lastDialog.push(sentence);
                dialogs.push(lastDialog);
            }
        }
        return {
            init: function() {
                var sentences = [];
                var sentence = new Sentence('Hello from the NLP system. Input your command please. The instructions are on the left.', 'sys');
                push(sentence, currentState);
                return getCurSentence();
            },
            getCurDialog: function() {
                return getCurDialog();
            },
            getCurSentence: function() {
                return getCurSentence();
            },
            push: function(sentence, state) {
                return push(sentence, state);
            },
            getLastSysSentence: function() {
                return getLastSysSentence();
            },
            getLastUsrSentence: function(n) {
                return getLastUsrSentence(n);
            }
        }
    };

    var parseToSentence = function(content) {
        var owner = 'usr';
        var pieces = getPieces(content);
        var preps = getPreps(content);
        var dets = getDets(content);
        var controlkey = getControlKey(content);
        var sentence = new Sentence(content, owner, pieces, preps, dets, controlkey);
        env.push(sentence, currentState);
        return sentence;
    };

    var getInfo = function(sentence) {
        var pieceLoc = sentence.pieces[0];
        var piece = game.get(pieceLoc);
        var output='';
        if (piece !== undefined && piece !== null) {
            output = 'There is a ' + dictR[piece.type] + ' on '+pieceLoc+'.';
        }
        else{
            output = 'There is no piece on '+ pieceLoc+'.';
        }
        return output;
    }

    //format the input
    var beautify = function(content) {
        content = content.trim()
            .toLowerCase()
            .replace(/\s+/g, '');
        return content;
    };

    //chess/chessboard control
    var move = function(source, target) {
        var generateOptions = function(pieces) {
            var output = '';
            _.each(pieces, function(piece) {
                output += piece.loc + ' / ';
            });
            output = output.replace(/\/\s+$/g, '');
            output = output.replace(/\s+$/g, '');
            output += '?';
            return output;
        }
        if (source.search(/[a-h][1-8]/) > -1) {
            onDrop(source, target);
            myboard.position(game.fen());
            return sysLog('Moved from ' + source + ' to ' + target + '.');
        } else {
            var availablePieces = [];
            _.each(game.SQUARES, function(piece) {
                var tmp = game.get(piece);
                if (tmp !== null && tmp !== undefined) {
                    if (dictR[tmp.type] === source && game.turn() === tmp.color) {
                        if (testLegal(piece, target) === true) {
                            availablePieces.push({
                                loc: piece,
                                piece: tmp
                            });
                        }
                    }
                }
            });
            if (availablePieces.length === 1) {
                onDrop(availablePieces[0].loc, target);
                myboard.position(game.fen());
                return sysLog('Moved ' + dictR[availablePieces[0].piece.type] + ' at ' + availablePieces[0].loc + ' to ' + target + '.');
            } else if (availablePieces.length > 1) {
                currentState = 'moreValidMoves';
                return sysLog('More than one valid move, ' + dictR[availablePieces[0].piece.type] + ' at ' + generateOptions(availablePieces));
            }
        }
    };

    var dispatcher = function(sentence, state, env) {
        switch (state.content) {
            case 'new':
                switch (sentence.intent) {
                    case 'control':
                        if ($.inArray('repeat', sentence.controlkey) > -1) {
                            return env.getLastSysSentence().content;
                        }
                        break;
                    case 'inquiry':
                        if (sentence.dets !== undefined && sentence.dets !== null) {
                            return getInfo(sentence);
                        }
                        break;
                    case 'move':
                        var pieces = sentence.pieces;
                        if (pieces.length === 2) {
                            return move(pieces[0], pieces[1]);
                        }
                        break;
                    case 'other':

                        break;
                    default:
                        console.log('bad switch intent');
                        return;
                }
                break;
            case 'moreValidMoves':
                var onlypiece = sentence.pieces[0];
                if (onlypiece !== null && onlypiece !== undefined) {
                    var target = env.getLastUsrSentence(2).pieces[1];
                    currentState = 'new';
                    return move(onlypiece, target);
                };
                break;
            default:
                return undefined;
        }
    }

    //public APIs
    return {
        init: function() {
            env = new ENV();
            currentState = 'new';
            return env.init();
        },
        input: function(content) {
            content = beautify(content);
            //content = errorCheck(content);
            var sentence = parseToSentence(content);
            if (sentence.intent !== null || sentence.intent !== undefined) {
                return dispatcher(sentence, {
                    content: currentState
                }, env);
            }
        },
        resetState: function() {
            if (currentState !== 'new') {
                currentState = 'new';
                return sysLog('New dialog begins.');
            }
            return undefined;
        }
    }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Client xss.util extension
 */
xss.util.extend(xss.util, /** @lends xss.util */ {

    /**
     * @param {string} str
     * @param {number=} duration
     * @param {boolean=} flash
     */
    instruct: function(str, duration, flash) {
        var shape, x, y;

        x = xss.WIDTH - xss.font.width(str) - 2;
        y = xss.HEIGHT - 2 - xss.font.height(str);

        shape = xss.font.shape(str, x, y, {invert: true});
        shape.isOverlay = true;
        shape.lifetime(0, duration);

        if (flash) {
            shape.flash();
        }

        xss.shapes.INSTRUCTION = shape;
    },

    /**
     * @param {string} str
     * @param {Function=} callback
     */
    error: function(str, callback) {
        var exit, dialog, body;

        xss.util.hash();

        exit = function() {
            dialog.destruct();
            if (callback) {
                callback();
            }
            xss.flow.restart();
        };

        body = 'Press ' + xss.UC_ENTER_KEY + ' to continue';
        dialog = new xss.Dialog(str, body, {
            type: xss.Dialog.TYPE.ALERT,
            ok: exit
        });
    },

    /**
     * Simple wrapper for localStorage
     * @param {string} key
     * @param {*?} value
     * @return {*}
     */
    storage: function(key, value) {
        if (arguments.length === 1) {
            try {
                return JSON.parse(localStorage.getItem(key));
            } catch (err) {
                localStorage.removeItem(key);
            }
            return '';
        } else if (arguments.length === 2) {
            if (value === null) {
                localStorage.removeItem(key);
                return '';
            } else {
                return localStorage.setItem(key, JSON.stringify(value));
            }
        }
    },

    /**
     * @return {boolean}
     */
    isMac: function() {
        return (/Macintosh/).test(navigator.appVersion);
    },

    /**
     * Simple wrapper for location.hash
     * @param {string?} key
     * @param {*?} value
     * @return {*}
     */
    hash: function(key, value) {
        var hash, arr, newhash = '', dict = {};

        hash = location.hash.substr(1);
        arr = hash.split(/[:;]/g);

        // Populate dict
        for (var i = 0, m = arr.length; i < m; i += 2) {
            dict[arr[i]] = arr[i + 1];
        }

        switch (arguments.length) {
            case 0: // Empty
                if (location.hash) {
                    history.replaceState(null, '', location.pathname + location.search);
                }
                return;
            case 1: // Return value
                return dict[key] || '';
            case 2: // Set value
                dict[key] = value;
                for (var k in dict) {
                    if (dict.hasOwnProperty(k)) {
                        if (k && dict[k]) {
                            newhash += k + ':' + dict[k] + ';';
                        }
                    }
                }
                location.replace('#' + newhash.replace(/;$/, ''));
                return value;
        }
    },

    /**
     * @param {number} num
     * @param {string=} single
     * @param {string=} plural
     * @return {string}
     */
    pluralize: function(num, single, plural) {
        return (num === 1) ? (single || '') : (plural || 's');
    },

    /**
     * @param {string} str
     * @param {...(string|number)} varArgs
     * @return {string}
     */
    format: function(str, varArgs) {
        var args = Array.prototype.slice.call(arguments, 1);
        return arguments[0].replace(/\{(\d+)\}/g, function(match, number) {
            return args[number];
        });
    },

    /**
     * @param {xss.Coordinate} coordinate
     * @return {xss.Coordinate}
     */
    translateGame: function(coordinate) {
        coordinate[0] = xss.util.translateGameX(coordinate[0]);
        coordinate[1] = xss.util.translateGameY(coordinate[1]);
        return coordinate;
    },

    /**
     * @param {number} x
     * @return {number}
     */
    translateGameX: function(x) {
        return (x * xss.GAME_TILE) + xss.GAME_LEFT;
    },

    /**
     * @param {number} y
     * @return {number}
     */
    translateGameY: function(y) {
        return (y * xss.GAME_TILE) + xss.GAME_TOP;
    },

    /**
     * @param {Function} fn
     * @param {number=} delay
     * @return {Function}
     */
    debounce: function(fn, delay) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(function() {
                timeout = null;
                fn.apply(context, args);
            }, delay || 100);
        };
    }

});
<commit_msg>Cleanup<commit_after>'use strict';

/**
 * Client xss.util extension
 */
xss.util.extend(xss.util, /** @lends xss.util */ {

    /**
     * @param {string} str
     * @param {number=} duration
     * @param {boolean=} flash
     */
    instruct: function(str, duration, flash) {
        var shape, x, y;

        x = xss.WIDTH - xss.font.width(str) - 2;
        y = xss.HEIGHT - xss.font.height(str) - 2;

        shape = xss.font.shape(str, x, y, {invert: true});
        shape.isOverlay = true;
        shape.lifetime(0, duration);

        if (flash) {
            shape.flash();
        }

        xss.shapes.INSTRUCTION = shape;
    },

    /**
     * @param {string} str
     * @param {Function=} callback
     */
    error: function(str, callback) {
        var exit, dialog, body;

        xss.util.hash();

        exit = function() {
            dialog.destruct();
            if (callback) {
                callback();
            }
            xss.flow.restart();
        };

        body = 'Press ' + xss.UC_ENTER_KEY + ' to continue';
        dialog = new xss.Dialog(str, body, {
            type: xss.Dialog.TYPE.ALERT,
            ok: exit
        });
    },

    /**
     * Simple wrapper for localStorage
     * @param {string} key
     * @param {*?} value
     * @return {*}
     */
    storage: function(key, value) {
        if (arguments.length === 1) {
            try {
                return JSON.parse(localStorage.getItem(key));
            } catch (err) {
                localStorage.removeItem(key);
            }
            return '';
        } else if (arguments.length === 2) {
            if (value === null) {
                localStorage.removeItem(key);
                return '';
            } else {
                return localStorage.setItem(key, JSON.stringify(value));
            }
        }
    },

    /**
     * @return {boolean}
     */
    isMac: function() {
        return (/Macintosh/).test(navigator.appVersion);
    },

    /**
     * Simple wrapper for location.hash
     * @param {string?} key
     * @param {*?} value
     * @return {*}
     */
    hash: function(key, value) {
        var hash, arr, newhash = '', dict = {};

        hash = location.hash.substr(1);
        arr = hash.split(/[:;]/g);

        // Populate dict
        for (var i = 0, m = arr.length; i < m; i += 2) {
            dict[arr[i]] = arr[i + 1];
        }

        switch (arguments.length) {
            case 0: // Empty
                if (location.hash) {
                    history.replaceState(null, '', location.pathname + location.search);
                }
                return;
            case 1: // Return value
                return dict[key] || '';
            case 2: // Set value
                dict[key] = value;
                for (var k in dict) {
                    if (dict.hasOwnProperty(k)) {
                        if (k && dict[k]) {
                            newhash += k + ':' + dict[k] + ';';
                        }
                    }
                }
                location.replace('#' + newhash.replace(/;$/, ''));
                return value;
        }
    },

    /**
     * @param {number} num
     * @param {string=} single
     * @param {string=} plural
     * @return {string}
     */
    pluralize: function(num, single, plural) {
        return (num === 1) ? (single || '') : (plural || 's');
    },

    /**
     * @param {string} str
     * @param {...(string|number)} varArgs
     * @return {string}
     */
    format: function(str, varArgs) {
        var args = Array.prototype.slice.call(arguments, 1);
        return arguments[0].replace(/\{(\d+)\}/g, function(match, number) {
            return args[number];
        });
    },

    /**
     * @param {xss.Coordinate} coordinate
     * @return {xss.Coordinate}
     */
    translateGame: function(coordinate) {
        coordinate[0] = xss.util.translateGameX(coordinate[0]);
        coordinate[1] = xss.util.translateGameY(coordinate[1]);
        return coordinate;
    },

    /**
     * @param {number} x
     * @return {number}
     */
    translateGameX: function(x) {
        return (x * xss.GAME_TILE) + xss.GAME_LEFT;
    },

    /**
     * @param {number} y
     * @return {number}
     */
    translateGameY: function(y) {
        return (y * xss.GAME_TILE) + xss.GAME_TOP;
    },

    /**
     * @param {Function} fn
     * @param {number=} delay
     * @return {Function}
     */
    debounce: function(fn, delay) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(function() {
                timeout = null;
                fn.apply(context, args);
            }, delay || 100);
        };
    }

});
<|endoftext|>


<commit_before>describe("liveSearch", function(){
  var $form, $results, _supportHistory, liveSearch;
  var dummyResponse = {
    "total":1,
    "pluralised_document_noun":"reports",
    "applied_filters":" \u003Cstrong\u003ECommercial - rotorcraft \u003Ca href='?format=json\u0026keywords='\u003E×\u003C/a\u003E\u003C/strong\u003E",
    "any_filters_applied":true,
    "documents":[
      {
        "title":"Test report",
        "slug":"aaib-reports/test-report",
        "metadata":[
          {
            "label":"Aircraft category",
            "value":"General aviation - rotorcraft",
            "is_text":true
          },{
            "label":"Report type",
            "value":"Annual safety report",
            "is_text":true
          },{
            "label":"Occurred",
            "is_date":true,
            "machine_date":"2013-11-03",
            "human_date":"3 November 2013"
          }
        ]
      }
    ]
  };

  beforeEach(function () {
    $form = $('<form action="/somewhere" class="js-live-search-form"><input type="checkbox" name="field" value="sheep" checked><input type="submit" value="Filter results" class="button js-live-search-fallback"/></form>');
    $results = $('

');
    $count = $('

');
    $('body').append($form).append($results);

    _supportHistory = GOVUK.support.history;
    GOVUK.support.history = function(){ return true; };

    liveSearch = new GOVUK.LiveSearch({$form: $form, $results: $results});
  });

  afterEach(function(){
    $form.remove();
    $results.remove();

    GOVUK.support.history = _supportHistory;
  });

  it("should save initial state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
  });

  it("should detect a new state", function(){
    expect(liveSearch.isNewState()).toBe(false);
    $form.find('input').prop('checked', false);
    expect(liveSearch.isNewState()).toBe(true);
  });

  it("should update state to current state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
    $form.find('input').prop('checked', false);
    liveSearch.saveState();
    expect(liveSearch.state).toEqual([]);
  });

  it("should update state to passed in state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
    $form.find('input').prop('checked', false);
    liveSearch.saveState({ my: "new", state: "object"});
    expect(liveSearch.state).toEqual({ my: "new", state: "object"});
  });

  it("should not request new results if they are in the cache", function(){
    liveSearch.resultCache["more=results"] = "exists";
    liveSearch.state = { more: "results" };
    spyOn(liveSearch, 'displayResults');
    spyOn(jQuery, 'ajax');

    liveSearch.updateResults();
    expect(liveSearch.displayResults).toHaveBeenCalled();
    expect(jQuery.ajax).not.toHaveBeenCalled();
  });

  it("should return a promise like object if results are in the cache", function(){
    liveSearch.resultCache["more=results"] = "exists";
    liveSearch.state = { more: "results" };
    spyOn(liveSearch, 'displayResults');
    spyOn(jQuery, 'ajax');

    var promise = liveSearch.updateResults();
    expect(typeof promise.done).toBe('function');
  });

  it("should return a promise like object if results aren't in the cache", function(){
    liveSearch.state = { not: "cached" };
    spyOn(liveSearch, 'displayResults');
    var ajaxCallback = jasmine.createSpyObj('ajax', ['done', 'error']);
    ajaxCallback.done.and.returnValue(ajaxCallback);
    spyOn(jQuery, 'ajax').and.returnValue(ajaxCallback);

    liveSearch.updateResults();
    expect(jQuery.ajax).toHaveBeenCalledWith({url: '/somewhere.json', data: {not: "cached"}});
    expect(ajaxCallback.done).toHaveBeenCalled();
    ajaxCallback.done.calls.mostRecent().args[0]('response data')
    expect(liveSearch.displayResults).toHaveBeenCalled();
    expect(liveSearch.resultCache['not=cached']).toBe('response data');
  });

  it("should show error indicator when error loading new results", function(){
    liveSearch.state = { not: "cached" };
    spyOn(liveSearch, 'displayResults');
    spyOn(liveSearch, 'showErrorIndicator');
    var ajaxCallback = jasmine.createSpyObj('ajax', ['done', 'error']);
    ajaxCallback.done.and.returnValue(ajaxCallback);
    spyOn(jQuery, 'ajax').and.returnValue(ajaxCallback);

    liveSearch.updateResults();
    ajaxCallback.error.calls.mostRecent().args[0]()
    expect(liveSearch.showErrorIndicator).toHaveBeenCalled();
  });

  it("should return cache items for current state", function(){
    liveSearch.state = { not: "cached" };
    expect(liveSearch.cache('some-slug')).toBe(undefined);
    liveSearch.cache('some-slug', 'something in the cache');
    expect(liveSearch.cache('some-slug')).toBe('something in the cache');
  });

  it("should show the filter results button if the GOVUK.support.history returns false", function(){
    // Hide the filter button (this is done in the CSS under the .js-enabled selector normally)
    $form.find('.js-live-search-fallback').hide();
    expect($form.find('.js-live-search-fallback').is(":visible")).toBe(false);
    GOVUK.support.history = function(){ return false; };
    liveSearch = new GOVUK.LiveSearch({$form: $form, $results: $results});
    expect($form.find('.js-live-search-fallback').is(":visible")).toBe(true);
  });

  describe('with relevant DOM nodes set', function(){
    beforeEach(function(){
      liveSearch.$form = $form;
      liveSearch.$resultsBlock = $results;
      liveSearch.$countBlock = $count;
      liveSearch.state = { field: "sheep" };
    });

    it("should update save state and update results when checkbox is changed", function(){
      var promise = jasmine.createSpyObj('promise', ['done']);
      spyOn(liveSearch, 'updateResults').and.returnValue(promise);
      //spyOn(liveSearch, 'pageTrack').and.returnValue(promise);
      $form.find('input').prop('checked', false);

      liveSearch.formChange();
      expect(liveSearch.state).toEqual([]);
      expect(liveSearch.updateResults).toHaveBeenCalled();
      promise.done.calls.mostRecent().args[0]();
      //expect(liveSearch.pageTrack).toHaveBeenCalled();
    });

    it("should do nothing if state hasn't changed when a checkbox is changed", function(){
      spyOn(liveSearch, 'updateResults');
      liveSearch.formChange();
      expect(liveSearch.state).toEqual({ field: 'sheep'});
      expect(liveSearch.updateResults).not.toHaveBeenCalled();
    });

    it("should display results from the cache", function(){
      liveSearch.resultCache["the=first"] = dummyResponse;
      liveSearch.state = { the: "first" };
      liveSearch.displayResults(dummyResponse);
      expect($results.find('h3').text()).toBe('Test report');
      expect($count.find('.result-count').text()).toMatch(/^\s*1\s*/);
    });
  });

  describe("popState", function(){
    var dummyHistoryState;

    beforeEach(function(){
      dummyHistoryState = { originalEvent:{ state:true} };
    });

    it("should call restoreBooleans, restoreTextInputs, saveState and updateResults if there is an event in the history", function(){
      spyOn(liveSearch, 'restoreBooleans');
      spyOn(liveSearch, 'restoreTextInputs');
      spyOn(liveSearch, 'saveState');
      spyOn(liveSearch, 'updateResults');

      liveSearch.popState(dummyHistoryState);

      expect(liveSearch.restoreBooleans).toHaveBeenCalled();
      expect(liveSearch.restoreTextInputs).toHaveBeenCalled();
      expect(liveSearch.saveState).toHaveBeenCalled();
      expect(liveSearch.updateResults).toHaveBeenCalled();
    });
  });

  describe("restoreBooleans", function(){
    beforeEach(function(){
      liveSearch.state = [{name:"list_1[]", value:"checkbox_1"}, {name:"list_1[]", value:"checkbox_2"}, {name:'list_2[]', value:"radio_1"}]
      liveSearch.$form = $('<form action="/somewhere" class="js-live-search-form"><input id="check_1" type="checkbox" name="list_1[]" value="checkbox_1"><input type="checkbox" id="check_2"  name="list_1[]" value="checkbox_2"><input type="radio" id="radio_1"  name="list_2[]" value="radio_1"><input type="radio" id="radio_2"  name="list_2[]" value="radio_2"><input type="submit"/></form>');
    });

    it("should check a checkbox if in the state it is checked in the history", function(){
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(2)
    });

    it("should not check all the checkboxes if only one is checked", function(){
      liveSearch.state = [{name:"list_1[]", value:"checkbox_2"}]
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=checkbox]:checked')[0].id).toBe('check_2');
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(1)
    });

    it("should pick a radiobox if in the state it is picked in the history", function(){
      expect(liveSearch.$form.find('input[type=radio]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=radio]:checked').length).toBe(1)
    });
  });

  describe("restoreKeywords", function(){
    beforeEach(function(){
       liveSearch.state = [{name:"text_1", value:"Monday"}]
       liveSearch.$form = $('<form action="/somewhere"><input id="text_1" type="text" name="text_1"><input id="text_2" type="text" name="text_2"></form>');
     });

     it("should put the right text back in the right box", function(){
       expect(liveSearch.$form.find('#text_1').val()).toBe('');
       expect(liveSearch.$form.find('#text_2').val()).toBe('');
       liveSearch.restoreTextInputs();
       expect(liveSearch.$form.find('#text_1').val()).toBe('Monday');
       expect(liveSearch.$form.find('#text_2').val()).toBe('');
     })
  });
});
<commit_msg>Add failing test for auto-update bug<commit_after>describe("liveSearch", function(){
  var $form, $results, _supportHistory, liveSearch;
  var dummyResponse = {
    "total":1,
    "pluralised_document_noun":"reports",
    "applied_filters":" \u003Cstrong\u003ECommercial - rotorcraft \u003Ca href='?format=json\u0026keywords='\u003E×\u003C/a\u003E\u003C/strong\u003E",
    "any_filters_applied":true,
    "documents":[
      {
        "title":"Test report",
        "slug":"aaib-reports/test-report",
        "metadata":[
          {
            "label":"Aircraft category",
            "value":"General aviation - rotorcraft",
            "is_text":true
          },{
            "label":"Report type",
            "value":"Annual safety report",
            "is_text":true
          },{
            "label":"Occurred",
            "is_date":true,
            "machine_date":"2013-11-03",
            "human_date":"3 November 2013"
          }
        ]
      }
    ]
  };

  beforeEach(function () {
    $form = $('<form action="/somewhere" class="js-live-search-form"><input type="checkbox" name="field" value="sheep" checked><input type="submit" value="Filter results" class="button js-live-search-fallback"/></form>');
    $results = $('

');
    $count = $('

');
    $('body').append($form).append($results);

    _supportHistory = GOVUK.support.history;
    GOVUK.support.history = function(){ return true; };

    liveSearch = new GOVUK.LiveSearch({$form: $form, $results: $results});
  });

  afterEach(function(){
    $form.remove();
    $results.remove();

    GOVUK.support.history = _supportHistory;
  });

  it("should save initial state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
  });

  it("should detect a new state", function(){
    expect(liveSearch.isNewState()).toBe(false);
    $form.find('input').prop('checked', false);
    expect(liveSearch.isNewState()).toBe(true);
  });

  it("should update state to current state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
    $form.find('input').prop('checked', false);
    liveSearch.saveState();
    expect(liveSearch.state).toEqual([]);
  });

  it("should update state to passed in state", function(){
    expect(liveSearch.state).toEqual([{name: 'field', value: 'sheep'}]);
    $form.find('input').prop('checked', false);
    liveSearch.saveState({ my: "new", state: "object"});
    expect(liveSearch.state).toEqual({ my: "new", state: "object"});
  });

  it("should not request new results if they are in the cache", function(){
    liveSearch.resultCache["more=results"] = "exists";
    liveSearch.state = { more: "results" };
    spyOn(liveSearch, 'displayResults');
    spyOn(jQuery, 'ajax');

    liveSearch.updateResults();
    expect(liveSearch.displayResults).toHaveBeenCalled();
    expect(jQuery.ajax).not.toHaveBeenCalled();
  });

  it("should return a promise like object if results are in the cache", function(){
    liveSearch.resultCache["more=results"] = "exists";
    liveSearch.state = { more: "results" };
    spyOn(liveSearch, 'displayResults');
    spyOn(jQuery, 'ajax');

    var promise = liveSearch.updateResults();
    expect(typeof promise.done).toBe('function');
  });

  it("should return a promise like object if results aren't in the cache", function(){
    liveSearch.state = { not: "cached" };
    spyOn(liveSearch, 'displayResults');
    var ajaxCallback = jasmine.createSpyObj('ajax', ['done', 'error']);
    ajaxCallback.done.and.returnValue(ajaxCallback);
    spyOn(jQuery, 'ajax').and.returnValue(ajaxCallback);

    liveSearch.updateResults();
    expect(jQuery.ajax).toHaveBeenCalledWith({url: '/somewhere.json', data: {not: "cached"}});
    expect(ajaxCallback.done).toHaveBeenCalled();
    ajaxCallback.done.calls.mostRecent().args[0]('response data')
    expect(liveSearch.displayResults).toHaveBeenCalled();
    expect(liveSearch.resultCache['not=cached']).toBe('response data');
  });

  it("should show error indicator when error loading new results", function(){
    liveSearch.state = { not: "cached" };
    spyOn(liveSearch, 'displayResults');
    spyOn(liveSearch, 'showErrorIndicator');
    var ajaxCallback = jasmine.createSpyObj('ajax', ['done', 'error']);
    ajaxCallback.done.and.returnValue(ajaxCallback);
    spyOn(jQuery, 'ajax').and.returnValue(ajaxCallback);

    liveSearch.updateResults();
    ajaxCallback.error.calls.mostRecent().args[0]()
    expect(liveSearch.showErrorIndicator).toHaveBeenCalled();
  });

  it("should return cache items for current state", function(){
    liveSearch.state = { not: "cached" };
    expect(liveSearch.cache('some-slug')).toBe(undefined);
    liveSearch.cache('some-slug', 'something in the cache');
    expect(liveSearch.cache('some-slug')).toBe('something in the cache');
  });

  describe("should not display out of date results", function(){

    it('should not update the results if the state associated with these results is not the current state of the page', function(){
      liveSearch.state = 'cma-cases.json?keywords=123'
      spyOn(liveSearch.$resultsBlock, 'mustache');
      liveSearch.displayResults(dummyResponse, 'made up state');
      expect(liveSearch.$resultsBlock.mustache).not.toHaveBeenCalled();
    });

    it('should update the results if the state of these results matches the state of the page', function(){
      liveSearch.state = 'cma-cases.json?keywords=123'
      spyOn(liveSearch.$resultsBlock, 'mustache');
      liveSearch.displayResults(dummyResponse, liveSearch.state);
      expect(liveSearch.$resultsBlock.mustache).toHaveBeenCalled();
    });
  });

  it("should show the filter results button if the GOVUK.support.history returns false", function(){
    // Hide the filter button (this is done in the CSS under the .js-enabled selector normally)
    $form.find('.js-live-search-fallback').hide();
    expect($form.find('.js-live-search-fallback').is(":visible")).toBe(false);
    GOVUK.support.history = function(){ return false; };
    liveSearch = new GOVUK.LiveSearch({$form: $form, $results: $results});
    expect($form.find('.js-live-search-fallback').is(":visible")).toBe(true);
  });

  describe('with relevant DOM nodes set', function(){
    beforeEach(function(){
      liveSearch.$form = $form;
      liveSearch.$resultsBlock = $results;
      liveSearch.$countBlock = $count;
      liveSearch.state = { field: "sheep" };
    });

    it("should update save state and update results when checkbox is changed", function(){
      var promise = jasmine.createSpyObj('promise', ['done']);
      spyOn(liveSearch, 'updateResults').and.returnValue(promise);
      //spyOn(liveSearch, 'pageTrack').and.returnValue(promise);
      $form.find('input').prop('checked', false);

      liveSearch.formChange();
      expect(liveSearch.state).toEqual([]);
      expect(liveSearch.updateResults).toHaveBeenCalled();
      promise.done.calls.mostRecent().args[0]();
      //expect(liveSearch.pageTrack).toHaveBeenCalled();
    });

    it("should do nothing if state hasn't changed when a checkbox is changed", function(){
      spyOn(liveSearch, 'updateResults');
      liveSearch.formChange();
      expect(liveSearch.state).toEqual({ field: 'sheep'});
      expect(liveSearch.updateResults).not.toHaveBeenCalled();
    });

    it("should display results from the cache", function(){
      liveSearch.resultCache["the=first"] = dummyResponse;
      liveSearch.state = { the: "first" };
      liveSearch.displayResults(dummyResponse);
      expect($results.find('h3').text()).toBe('Test report');
      expect($count.find('.result-count').text()).toMatch(/^\s*1\s*/);
    });
  });

  describe("popState", function(){
    var dummyHistoryState;

    beforeEach(function(){
      dummyHistoryState = { originalEvent:{ state:true} };
    });

    it("should call restoreBooleans, restoreTextInputs, saveState and updateResults if there is an event in the history", function(){
      spyOn(liveSearch, 'restoreBooleans');
      spyOn(liveSearch, 'restoreTextInputs');
      spyOn(liveSearch, 'saveState');
      spyOn(liveSearch, 'updateResults');

      liveSearch.popState(dummyHistoryState);

      expect(liveSearch.restoreBooleans).toHaveBeenCalled();
      expect(liveSearch.restoreTextInputs).toHaveBeenCalled();
      expect(liveSearch.saveState).toHaveBeenCalled();
      expect(liveSearch.updateResults).toHaveBeenCalled();
    });
  });

  describe("restoreBooleans", function(){
    beforeEach(function(){
      liveSearch.state = [{name:"list_1[]", value:"checkbox_1"}, {name:"list_1[]", value:"checkbox_2"}, {name:'list_2[]', value:"radio_1"}]
      liveSearch.$form = $('<form action="/somewhere" class="js-live-search-form"><input id="check_1" type="checkbox" name="list_1[]" value="checkbox_1"><input type="checkbox" id="check_2"  name="list_1[]" value="checkbox_2"><input type="radio" id="radio_1"  name="list_2[]" value="radio_1"><input type="radio" id="radio_2"  name="list_2[]" value="radio_2"><input type="submit"/></form>');
    });

    it("should check a checkbox if in the state it is checked in the history", function(){
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(2)
    });

    it("should not check all the checkboxes if only one is checked", function(){
      liveSearch.state = [{name:"list_1[]", value:"checkbox_2"}]
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=checkbox]:checked')[0].id).toBe('check_2');
      expect(liveSearch.$form.find('input[type=checkbox]:checked').length).toBe(1)
    });

    it("should pick a radiobox if in the state it is picked in the history", function(){
      expect(liveSearch.$form.find('input[type=radio]:checked').length).toBe(0)
      liveSearch.restoreBooleans();
      expect(liveSearch.$form.find('input[type=radio]:checked').length).toBe(1)
    });
  });

  describe("restoreKeywords", function(){
    beforeEach(function(){
       liveSearch.state = [{name:"text_1", value:"Monday"}]
       liveSearch.$form = $('<form action="/somewhere"><input id="text_1" type="text" name="text_1"><input id="text_2" type="text" name="text_2"></form>');
     });

     it("should put the right text back in the right box", function(){
       expect(liveSearch.$form.find('#text_1').val()).toBe('');
       expect(liveSearch.$form.find('#text_2').val()).toBe('');
       liveSearch.restoreTextInputs();
       expect(liveSearch.$form.find('#text_1').val()).toBe('Monday');
       expect(liveSearch.$form.find('#text_2').val()).toBe('');
     })
  });
});
<|endoftext|>