<commit_before>CATARSE.ProjectsNewView = Backbone.View.extend({
  initialize: function() {
    $.datepicker.setDefaults($.datepicker.regional[CATARSE.locale]);
    $('#add_reward').click(function(e){
      e.preventDefault()
      var new_reward = '

' + $($('.reward')[0]).html() + '
'
      new_reward = new_reward.replace(/\_0\_/g, '_' + rewards_id + '_')
      new_reward = new_reward.replace(/\[0\]/g, '[' + rewards_id + ']')
      new_reward = $(new_reward)
      new_reward.find('input').val(null)
      new_reward.find('textarea').html(null)
      new_reward.find('input').numeric(false)
      new_reward.find('input, textarea').removeClass('ok').removeClass('error')
      $('#rewards_wrapper').append(new_reward)
      new_reward.find('textarea').focus()
      rewards_count++
      rewards_id++
    })
    var video_valid = null
    everything_ok = function(){
      var all_ok = true
      if(video_valid == null) {
        all_ok = false
        verify_video()
      }
      if(!ok('#project_name'))
        all_ok = false
      if(!video_ok())
        all_ok = false
      if(!ok('#project_about'))
        all_ok = false
      if(!headline_ok())
        all_ok = false
      if(!ok('#project_category_id'))
        all_ok = false
      if(!goal_ok())
        all_ok = false
      if(!expires_at_ok())
        all_ok = false
      if(!rewards_ok())
        all_ok = false
      if(!accepted_terms())
        all_ok = false
      if(all_ok){
        $('#project_submit').attr('disabled', false)
      } else {
        $('#project_submit').attr('disabled', true)
      }
    }
    ok = function(id){
      var value = $(id).val()
      if(value && value.length > 0){
        $(id).addClass("ok").removeClass("error")
        return true
      } else {
        $(id).addClass("error").removeClass("ok")
        return false
      }
    }
    verify_video = function(){
      video_valid = false
      if(/http:\/\/(www\.)?vimeo.com\/(\d+)/.test($('#project_video_url').val())) {
        $('#project_video_url').removeClass("ok").removeClass("error").addClass('loading')
        $.get('/projects/vimeo/?url='+$('#project_video_url').val(), function(r){
          $('#project_video_url').removeClass("loading")
          if(r.id==false){
            video_valid = false
          } else {
            video_valid = true
          }
          everything_ok()
        })
      }
      everything_ok()
    }
    video_ok = function(){
      if(video_valid){
        $('#project_video_url').addClass("ok").removeClass("error")
        return true
      } else {
        if(!$('#project_video_url').hasClass('loading'))
          $('#project_video_url').addClass("error").removeClass("ok")
        return false
      }
    }
    headline_ok = function(){
      var value = $('#project_headline').val()
      if(value && value.length > 0 && value.length <= 140){
        $('#project_headline').addClass("ok").removeClass("error")
        return true
      } else {
        $('#project_headline').addClass("error").removeClass("ok")
        return false
      }
    }
    goal_ok = function(){
      var value = $('#project_goal').val()
      if(/^(\d+)$/.test(value) && parseInt(value) > 0){
        $('#project_goal').addClass("ok").removeClass("error")
        return true
      } else {
        $('#project_goal').addClass("error").removeClass("ok")
        return false
      }
    }
    expires_at_ok = function(){
      var value = /^(\d{2})\/(\d{2})\/(\d{4})?$/.exec($('#project_expires_at').val())
      if(value && value.length == 4) {
        var year = parseFloat(value[3])
        var month = parseFloat(value[2])-1
        var day = parseFloat(value[1])
        var date = new Date(year, month, day)
        var current_date = new Date()
        if(((day==date.getDate()) && (month==date.getMonth()) && (year==date.getFullYear())) && date > current_date){
          $('#project_expires_at').addClass("ok").removeClass("error")
          return true
        } else {
          $('#project_expires_at').addClass("error").removeClass("ok")
          return false
        }
      } else {
        $('#project_expires_at').addClass("error").removeClass("ok")
        return false
      }
    }
    rewards_ok = function(){
      var okey = true
      $('.reward input').each(function(){
        if(/^(\d+)$/.test($(this).val())){
          if(/minimum_value/.test($(this).attr('id'))){
            if(parseInt($(this).val()) >= 10) {
              $(this).addClass("ok").removeClass("error")
            } else {
              $(this).addClass("error").removeClass("ok")
              okey = false
            }
          } else {
            $(this).addClass("ok").removeClass("error")
          }
        } else {
          if(/maximum_backers/.test($(this).attr('id')) && (!$(this).val())){
            $(this).addClass("ok").removeClass("error")
          } else {
            $(this).addClass("error").removeClass("ok")
            okey = false
          }
        }
      })
      $('.reward textarea').each(function(){
        if($(this).val() && $(this).val().length > 0){
          $(this).addClass("ok").removeClass("error")
        } else {
          $(this).addClass("error").removeClass("ok")
          okey = false
        }
      })
      return okey
    }
    accepted_terms = function(){
      return $('#accept').is(':checked')
    }
    $('#project_name').keyup(everything_ok)
    $('#project_video_url').keyup(function(){ video_valid = false; everything_ok() })
    $('#project_video_url').timedKeyup(verify_video)
    $('#project_about').keyup(everything_ok)
    $('#project_category_id').change(everything_ok)
    $('#project_goal').keyup(everything_ok)
    $('#project_expires_at').keyup(everything_ok)
    $('#project_headline').keyup(everything_ok)
    $('#accept').click(everything_ok)
    $('.reward input,.reward textarea').live('keyup', everything_ok)

    $('#project_goal').numeric(false)
    $('.reward input').numeric(false)
    $('#project_expires_at').datepicker({
      altFormat: 'dd/mm/yy',
      onSelect: everything_ok
    })
    $('#ui-datepicker-div').css('display', 'none')
    $('input,textarea,select').live('focus', function(){
      $('p.inline-hints').hide()
      $(this).next('p.inline-hints').show()
    })
    $('.reward').live('mouseover', function(){
      $('.remove_reward').hide()
      if(rewards_count > 1){
        $(this).find('.remove_reward').show()
      }
    })
    $('.reward').live('mouseout', function(){
      $(this).find('.remove_reward').hide()
    })
    $('.remove_reward').live('click', function(e){
      e.preventDefault()
      if(rewards_count > 1){
        reward = $(this).parentsUntil('.reward').parent()
        reward.remove()
        rewards_count--
      }
    })
    $('#project_name').focus()
    $('textarea').maxlength()
  }
})
<commit_msg>added the image V for address fields in project/new<commit_after>CATARSE.ProjectsNewView = Backbone.View.extend({
  initialize: function() {
    $.datepicker.setDefaults($.datepicker.regional[CATARSE.locale]);
    $('#add_reward').click(function(e){
      e.preventDefault()
      var new_reward = '

' + $($('.reward')[0]).html() + '
'
      new_reward = new_reward.replace(/\_0\_/g, '_' + rewards_id + '_')
      new_reward = new_reward.replace(/\[0\]/g, '[' + rewards_id + ']')
      new_reward = $(new_reward)
      new_reward.find('input').val(null)
      new_reward.find('textarea').html(null)
      new_reward.find('input').numeric(false)
      new_reward.find('input, textarea').removeClass('ok').removeClass('error')
      $('#rewards_wrapper').append(new_reward)
      new_reward.find('textarea').focus()
      rewards_count++
      rewards_id++
    })
    var video_valid = null
    everything_ok = function(){
      var all_ok = true
      if(video_valid == null) {
        all_ok = false
        verify_video()
      }
      if(!ok('#project_name'))
        all_ok = false
      if(!video_ok())
        all_ok = false
      if(!ok('#project_about'))
        all_ok = false
      if(!headline_ok())
        all_ok = false
      if(!ok('#project_category_id'))
        all_ok = false
      if(!goal_ok())
        all_ok = false
      if(!expires_at_ok())
        all_ok = false
      if(!rewards_ok())
        all_ok = false
      if(!accepted_terms())
        all_ok = false
      if(all_ok){
        $('#project_submit').attr('disabled', false)
      } else {
        $('#project_submit').attr('disabled', true)
      }
      $('#project_address_city, #project_address_state, #project_address_country').addClass("ok").removeClass("error")
    }
    ok = function(id){
      var value = $(id).val()
      if(value && value.length > 0){
        $(id).addClass("ok").removeClass("error")
        return true
      } else {
        $(id).addClass("error").removeClass("ok")
        return false
      }
    }
    verify_video = function(){
      video_valid = false
      if(/http:\/\/(www\.)?vimeo.com\/(\d+)/.test($('#project_video_url').val())) {
        $('#project_video_url').removeClass("ok").removeClass("error").addClass('loading')
        $.get('/projects/vimeo/?url='+$('#project_video_url').val(), function(r){
          $('#project_video_url').removeClass("loading")
          if(r.id==false){
            video_valid = false
          } else {
            video_valid = true
          }
          everything_ok()
        })
      }
      everything_ok()
    }
    video_ok = function(){
      if(video_valid){
        $('#project_video_url').addClass("ok").removeClass("error")
        return true
      } else {
        if(!$('#project_video_url').hasClass('loading'))
          $('#project_video_url').addClass("error").removeClass("ok")
        return false
      }
    }
    headline_ok = function(){
      var value = $('#project_headline').val()
      if(value && value.length > 0 && value.length <= 140){
        $('#project_headline').addClass("ok").removeClass("error")
        return true
      } else {
        $('#project_headline').addClass("error").removeClass("ok")
        return false
      }
    }
    goal_ok = function(){
      var value = $('#project_goal').val()
      if(/^(\d+)$/.test(value) && parseInt(value) > 0){
        $('#project_goal').addClass("ok").removeClass("error")
        return true
      } else {
        $('#project_goal').addClass("error").removeClass("ok")
        return false
      }
    }
    expires_at_ok = function(){
      var value = /^(\d{2})\/(\d{2})\/(\d{4})?$/.exec($('#project_expires_at').val())
      if(value && value.length == 4) {
        var year = parseFloat(value[3])
        var month = parseFloat(value[2])-1
        var day = parseFloat(value[1])
        var date = new Date(year, month, day)
        var current_date = new Date()
        if(((day==date.getDate()) && (month==date.getMonth()) && (year==date.getFullYear())) && date > current_date){
          $('#project_expires_at').addClass("ok").removeClass("error")
          return true
        } else {
          $('#project_expires_at').addClass("error").removeClass("ok")
          return false
        }
      } else {
        $('#project_expires_at').addClass("error").removeClass("ok")
        return false
      }
    }
    rewards_ok = function(){
      var okey = true
      $('.reward input').each(function(){
        if(/^(\d+)$/.test($(this).val())){
          if(/minimum_value/.test($(this).attr('id'))){
            if(parseInt($(this).val()) >= 10) {
              $(this).addClass("ok").removeClass("error")
            } else {
              $(this).addClass("error").removeClass("ok")
              okey = false
            }
          } else {
            $(this).addClass("ok").removeClass("error")
          }
        } else {
          if(/maximum_backers/.test($(this).attr('id')) && (!$(this).val())){
            $(this).addClass("ok").removeClass("error")
          } else {
            $(this).addClass("error").removeClass("ok")
            okey = false
          }
        }
      })
      $('.reward textarea').each(function(){
        if($(this).val() && $(this).val().length > 0){
          $(this).addClass("ok").removeClass("error")
        } else {
          $(this).addClass("error").removeClass("ok")
          okey = false
        }
      })
      return okey
    }
    accepted_terms = function(){
      return $('#accept').is(':checked')
    }
    $('#project_name').keyup(everything_ok)
    $('#project_video_url').keyup(function(){ video_valid = false; everything_ok() })
    $('#project_video_url').timedKeyup(verify_video)
    $('#project_about').keyup(everything_ok)
    $('#project_category_id').change(everything_ok)
    $('#project_goal').keyup(everything_ok)
    $('#project_expires_at').keyup(everything_ok)
    $('#project_headline').keyup(everything_ok)
    $('#accept').click(everything_ok)
    $('.reward input,.reward textarea').live('keyup', everything_ok)
    $('#project_address_city, #project_address_state, #project_address_country').keyup(everything_ok)

    $('#project_goal').numeric(false)
    $('.reward input').numeric(false)
    $('#project_expires_at').datepicker({
      altFormat: 'dd/mm/yy',
      onSelect: everything_ok
    })
    $('#ui-datepicker-div').css('display', 'none')
    $('input,textarea,select').live('focus', function(){
      $('p.inline-hints').hide()
      $(this).next('p.inline-hints').show()
    })
    $('.reward').live('mouseover', function(){
      $('.remove_reward').hide()
      if(rewards_count > 1){
        $(this).find('.remove_reward').show()
      }
    })
    $('.reward').live('mouseout', function(){
      $(this).find('.remove_reward').hide()
    })
    $('.remove_reward').live('click', function(e){
      e.preventDefault()
      if(rewards_count > 1){
        reward = $(this).parentsUntil('.reward').parent()
        reward.remove()
        rewards_count--
      }
    })
    $('#project_name').focus()
    $('textarea').maxlength()
  }
})
<|endoftext|>


<commit_before>
Data.Adapter = Ember.Object.extend({

  baseUrl: Ember.required(),
  authParams: null,

  findOne: function (type, id, parent, options) {
    options = options || {};
    return this.findWithCache(options,
      function (ok) {
        var cached = type.cachedRecord(id);
        if (cached) {
          ok(cached);
          return true;
        }
        return false;
      },
      function (async, ok, ko) {
        Data.ajax({
          url: this.buildUrl(type, id, parent),
          type: 'GET',
          async: async,
          dataType: 'json',
          data: this.buildParams(options.params),
          success: function (data) {
            Ember.Logger.debug("DATA - Found one", type, "(" + id + "):", data);
            var resourceKey = type.resourceKey();

            if (data[resourceKey]) {
              var record = type.load(data[resourceKey]);
              type.sideLoad(data);
              ok(record);
            } else {
              var message = "API returned JSON with missing key '" + resourceKey + "'";
              Ember.Logger.error(message, data);
              ko(message);
            }
          },
          error: function (xhr, status, error) {
            Ember.Logger.error(xhr, status, error);
            ko(error);
          }
        });
      }
    );
  },

  findMany: function (type, ids, parent, options) {
    options = options || {};
    return this.findWithCache(options,
      function (ok) {
        var cached = ids.map(function (id) {
          return type.cachedRecord(id);
        }).compact();

        if (!Ember.isEmpty(ids) && (cached.length === ids.length)) {
          ok(cached);
          return true;
        }
        return false;
      },
      function (async, ok, ko) {
        Data.ajax({
          url: this.buildUrl(type, null, parent),
          type: 'GET',
          async: async,
          dataType: 'json',
          data: this.buildParams(options.params, {
            ids: ids, // Ember.isEmpty(ids) ? null : ids,
          }),
          success: function (data) {
            Ember.Logger.debug("DATA - Found many", type, (parent ? "(parent " + parent.toString() + ")" : '') + ":", data);
            var
              resourceKey = type.resourceKey().pluralize(),
              result = [];

            if (data[resourceKey]) {
              result.addObjects(data[resourceKey].map(function (itemData) {
                return type.load(itemData);
              }));
              type.sideLoad(data);
              ok(result);
            } else {
              Ember.Logger.error("API returned JSON with missing key '" + resourceKey + "'", data);
              ko();
            }
          },
          error: function (xhr, status, error) {
            Ember.Logger.error(xhr, status, error);
            ko(error);
          }
        });
      });
  },

  findWithCache: function (options, findInCache, find) {
    options = options || {};

    var
      adapter = this,
      async = !options.sync,
      noCache = options.noCache;

    function run (async, noCache, resolve, reject) {
      var result = null;

      function ok (record) {
        Ember.run(function () {
          if (async) {
            resolve(record);
          } else {
            result = record;
          }
        });
      }

      function ko (error) {
        Ember.run(function () {
          if (async) {
            reject(error);
          } else {
            result = null;
          }
        });
      }

      if (noCache || !findInCache.call(adapter, ok)) {
        find.call(adapter, async, ok, ko);
      }
      return result;
    }

    if (async) {
      return new Ember.RSVP.Promise(function (resolve, reject) {
        run(true, noCache, resolve, reject);
      });
    } else {
      return run(false, noCache);
    }
  },

  save: function (record, extraParams) {
    var
      adapter = this,
      url = [ adapter.get('baseUrl'), record.get('_url') ].join('/'),
      action,
      async = true,
      params = {},
      resourceKey = record.constructor.resourceKey();

    return new Ember.RSVP.Promise(function (resolve, reject) {
      Ember.run(function () {
        if (!(record.get('meta.isDirty') || record.get('meta.isNew'))) {
          Ember.Logger.warn('Do not save clean record: ' + record.toString());
          record.unload();
          resolve();
          return;
        }

        if (record.get('meta.isDeleted')) {
          action = 'DELETE';
        } else {
          params[resourceKey] = record;

          if (record.get('meta.isNew')) {
            action = 'POST';
          } else {
            action = 'PUT';
          }
        }

        Data.ajax({
          url: url,
          type: action,
          async: async,
         // dataType: 'json',
          contentType: 'application/json',
          data: JSON.stringify(adapter.buildParams(params, extraParams)),
          success: function (data) {
            Ember.run(function () {
              Ember.Logger.debug("DATA - Saved (" + action + ")",
                record.toString(), (parent ? "(parent " + parent.toString() + ")" : '') + ":", data);

              if (data[resourceKey]) {
                record._updateData(data[resourceKey]);
                record.constructor.sideLoad(data);
                resolve(record);
              } else {
                if (action === 'DELETE') {
                  record.unload();
                  resolve(record);
                } else {
                  Ember.Logger.error("API returned JSON with missing key '" + resourceKey + "'", data);
                  reject();
                }
              }
            });
          },
          error: function (xhr, status, error) {
            Ember.run(function () {
              Ember.Logger.error(xhr, status, error);
              reject(error);
            });
          }
        });
      });
    });
  },

  buildUrl: function (type, id, parent) {
    var urlParts = [
      this.get('baseUrl')
    ];

    if (parent) {
      urlParts.pushObject(parent.get('_url'));
    }
    urlParts.pushObject(type.resourceUrl());
    if (id) {
      urlParts.pushObject(id);
    }
    return urlParts.join('/');
  },

  buildParams: function (optionParams, extraParams) {
    var params = {};
    $.extend(params, this.get('authParams'));

    if (optionParams) {
      $.extend(params, optionParams);
    }
    if (extraParams) {
      $.extend(params, extraParams);
    }
    return params;
  },
});
<commit_msg>Refactor Data.ajax wrapping: add per-adapter customizable primitive<commit_after>
Data.Adapter = Ember.Object.extend({

  baseUrl: Ember.required(),
  authParams: null,

  /**
    Intended to be overriden as needed.
   */
  ajax: function (settings) {
    return Data.ajax(settings);
  },

  findOne: function (type, id, parent, options) {
    options = options || {};
    return this.findWithCache(options,
      function (ok) {
        var cached = type.cachedRecord(id);
        if (cached) {
          ok(cached);
          return true;
        }
        return false;
      },
      function (async, ok, ko) {
        var action = 'GET',
            url = this.buildUrl(type, id, parent);

        this.ajax({
          async: async,
          type: action,
          url: url,
          dataType: 'json',
          data: this.buildParams(options.params),
          success: function (data) {
            Ember.Logger.debug("DATA - Found one", type, "(" + id + "):", data);
            var resourceKey = type.resourceKey();

            if (data[resourceKey]) {
              var record = type.load(data[resourceKey]);
              type.sideLoad(data);
              ok(record);
            } else {
              var message = "API returned JSON with missing key '" + resourceKey + "'";
              Ember.Logger.error(message, data);
              ko(message);
            }
          },
          error: function (xhr, status, error) {
            Ember.Logger.error(status + ':', action, url, error);
            ko(error);
          }
        });
      }
    );
  },

  findMany: function (type, ids, parent, options) {
    options = options || {};
    return this.findWithCache(options,
      function (ok) {
        var cached = ids.map(function (id) {
          return type.cachedRecord(id);
        }).compact();

        if (!Ember.isEmpty(ids) && (cached.length === ids.length)) {
          ok(cached);
          return true;
        }
        return false;
      },
      function (async, ok, ko) {
        var action = 'GET',
            url = this.buildUrl(type, null, parent);

        this.ajax({
          async: async,
          type: action,
          url: url,
          dataType: 'json',
          data: this.buildParams(options.params, {
            ids: ids, // Ember.isEmpty(ids) ? null : ids,
          }),
          success: function (data) {
            Ember.Logger.debug("DATA - Found many", type, (parent ? "(parent " + parent.toString() + ")" : '') + ":", data);
            var resourceKey = type.resourceKey().pluralize(),
                result = [];

            if (data[resourceKey]) {
              result.addObjects(data[resourceKey].map(function (itemData) {
                return type.load(itemData);
              }));
              type.sideLoad(data);
              ok(result);
            } else {
              Ember.Logger.error("API returned JSON with missing key '" + resourceKey + "'", data);
              ko();
            }
          },
          error: function (xhr, status, error) {
            Ember.Logger.error(status + ':', action, url, error);
            ko(error);
          }
        });
      });
  },

  findWithCache: function (options, findInCache, find) {
    options = options || {};

    var adapter = this,
        async = !options.sync,
        noCache = options.noCache;

    function run (async, noCache, resolve, reject) {
      var result = null;

      function ok (record) {
        Ember.run(function () {
          if (async) {
            resolve(record);
          } else {
            result = record;
          }
        });
      }

      function ko (error) {
        Ember.run(function () {
          if (async) {
            reject(error);
          } else {
            result = null;
          }
        });
      }

      if (noCache || !findInCache.call(adapter, ok)) {
        find.call(adapter, async, ok, ko);
      }
      return result;
    }

    if (async) {
      return new Ember.RSVP.Promise(function (resolve, reject) {
        run(true, noCache, resolve, reject);
      });
    } else {
      return run(false, noCache);
    }
  },

  save: function (record, extraParams) {
    var adapter = this,
        url = [ adapter.get('baseUrl'), record.get('_url') ].join('/'),
        action,
        async = true,
        params = {},
        resourceKey = record.constructor.resourceKey();

    return new Ember.RSVP.Promise(function (resolve, reject) {
      Ember.run(adapter, function () {
        if (!(record.get('meta.isDirty') || record.get('meta.isNew'))) {
          Ember.Logger.warn('Do not save clean record: ' + record.toString());
          record.unload();
          resolve();
          return;
        }

        if (record.get('meta.isDeleted')) {
          action = 'DELETE';
        } else {
          params[resourceKey] = record;

          if (record.get('meta.isNew')) {
            action = 'POST';
          } else {
            action = 'PUT';
          }
        }

        this.ajax({
          async: async,
          type: action,
          url: url,
          // dataType: 'json',
          contentType: 'application/json',
          data: JSON.stringify(adapter.buildParams(params, extraParams)),
          success: function (data) {
            Ember.run(function () {
              Ember.Logger.debug("DATA - Saved (" + action + ")",
                record.toString(), (parent ? "(parent " + parent.toString() + ")" : '') + ":", data);

              if (data[resourceKey]) {
                record._updateData(data[resourceKey]);
                record.constructor.sideLoad(data);
                resolve(record);
              } else {
                if (action === 'DELETE') {
                  record.unload();
                  resolve(record);
                } else {
                  Ember.Logger.error("API returned JSON with missing key '" + resourceKey + "'", data);
                  reject();
                }
              }
            });
          },
          error: function (xhr, status, error) {
            Ember.run(function () {
              Ember.Logger.error(status + ':', action, url, error);
              reject(error);
            });
          }
        });
      });
    });
  },

  buildUrl: function (type, id, parent) {
    var urlParts = [
      this.get('baseUrl')
    ];

    if (parent) {
      urlParts.pushObject(parent.get('_url'));
    }
    urlParts.pushObject(type.resourceUrl());
    if (id) {
      urlParts.pushObject(id);
    }
    return urlParts.join('/');
  },

  buildParams: function (optionParams, extraParams) {
    var params = {};
    $.extend(params, this.get('authParams'));

    if (optionParams) {
      $.extend(params, optionParams);
    }
    if (extraParams) {
      $.extend(params, extraParams);
    }
    return params;
  },
});
<|endoftext|>


<commit_before>var boardId = $('#board-id').val();
var jobId = $('#job-id').val();
var kernelId = $('#kernel-id').val();
var defconfId = $('#defconfig-id').val();

function populatePage(data) {
    'use strict';

    var localData = data.result[0],
        bootTime,
        displ = '',
        fileServer = $('#file-server').val(),
        metadata,
        nonAvail = '' +
            '';

    bootTime = new Date(localData.time['$date']);
    metadata = localData.metadata;

    $('#dd-board-board').empty().append(localData.board);
    $('#dd-board-defconfig').empty().append(
        localData.defconfig + ' — ' +
            ''
    );
    $('#dd-board-kernel').empty().append(
        '' + localData.kernel +
            '' +
            ' — ' +
            ''
    );
    $('#dd-board-tree').empty().append(
        '' +
            '' + localData.job +
            '' +
            ' — ' +
            ''
    );

    if (localData.endian !== null) {
        $('#dd-board-endianness').empty().append(localData.endian);
    } else {
        $('#dd-board-endianness').empty().append(nonAvail);
    }

    if (localData.boot_log !== null || localData.boot_log_html !== null) {
        $('#dd-board-boot-log').empty();

        if (localData.boot_log !== null) {
            $('#dd-board-boot-log').append(
                'txt' +
                ' '
            );
        }

        if (localData.boot_log_html !== null) {
            if (localData.boot_log !== null) {
                $('#dd-board-boot-log').append(' — ');
            }
            $('#dd-board-boot-log').append(
                'html '
            );
        }
    } else {
        $('#dd-board-boot-log').empty().append(nonAvail);
    }

    switch (localData.status) {
        case 'PASS':
            displ = '';
            break;
        case 'FAIL':
            displ = '' +
                    '';
            break;
        case 'OFFLINE':
            displ = '<span rel="tooltip" data-toggle="tooltip"' +
                'title="Board offline" ' +
                '';
            break;
        default:
            displ = '' +
                    '';
            break;
    }

    // Do we have a description for the boot result?
    // We might have it directly in the json or in the metadata property.
    if (localData.hasOwnProperty('boot_result_description')) {
        displ += ' ' + localData.boot_result_description +
            '';
    } else if (! $.isEmptyObject(metadata) &&
            metadata.hasOwnProperty('boot_result_description')) {
        displ += ' ' + metadata.boot_result_description +
            '';
    }

    $('#dd-board-status').empty().append(displ);
    $('#dd-board-boot-time').empty().append(bootTime.getCustomTime());

    if (localData.warnings !== null) {
        $('#dd-board-warnings').empty().append(localData.warnings);
    } else {
        $('#dd-board-warnings').empty().append(0);
    }

    if (localData.dtb !== null && localData.dtb !== '') {
        $('#dd-board-dtb').empty().append(localData.dtb);
    } else {
        $('#dd-board-dtb').empty().append(nonAvail);
    }

    if (localData.dtb_addr !== null && localData.dtb_addr !== '') {
        $('#dd-board-dtb-address').empty().append(localData.dtb_addr);
    } else {
        $('#dd-board-dtb-address').empty().append(nonAvail);
    }

    if (localData.initrd_addr !== null && localData.initrd_addr !== '') {
        $('#dd-board-initrd-address').empty().append(localData.initrd_addr);
    } else {
        $('#dd-board-initrd-address').empty().append(nonAvail);
    }

    if (localData.load_addr !== null && localData.load_addr !== '') {
        $('#dd-board-load-address').empty().append(localData.load_addr);
    } else {
        $('#dd-board-load-address').empty().append(nonAvail);
    }

    if (localData.kernel_image !== null && localData.kernel_image !== '') {
        $('#dd-board-kernel-image').empty().append(localData.kernel_image);
    } else {
        $('#dd-board-kernel-image').empty().append(nonAvail);
    }
}

function createBisectScriptURI(badCommit, goodCommit) {
    'use strict';
    var bisectScript = '#!/bin/bash\n' +
        'git bisect start ' + badCommit + goodCommit + '\n';

    return 'data:text/plain;charset=UTF-8,' + encodeURIComponent(bisectScript);
}

function createBootBisectTable(data) {
    'use strict';
    $('#loading-content').empty().append("loading bisect data…");

    var localData = data.result,
        localLen = localData.length,
        i = 0,
        bisectData,
        gitDescribeCell,
        badCommitCell,
        unknownCommitCell,
        goodCommitCell,
        bootStatus,
        bisectDefMetadata,
        tableRows,
        tooltipLink,
        tooltipTitle,
        gitURLs,
        gitDescribeVal,
        badCommit = null,
        goodCommit = null;

    badCommit = localData[0].defconfig_metadata.git_commit;

    for (i; i < localLen; i++) {
        bisectData = localData[i];
        bootStatus = bisectData.boot_status;
        bisectDefMetadata = bisectData.defconfig_metadata;
        gitDescribeVal = bisectDefMetadata.git_describe;

        tooltipLink = '' +
            gitDescribeVal + '';

        tooltipTitle = 'Boot report details for ' + jobId +
            ' ‐ ' + gitDescribeVal;

        gitDescribeCell = '' +
            '' +
            '' + tooltipLink +
            '	';

        gitURLs = translateCommitURL(
            bisectDefMetadata.git_url, bisectDefMetadata.git_commit);

        switch (bootStatus) {
            case 'PASS':
                goodCommit = bisectDefMetadata.git_commit;
                goodCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                badCommitCell = '	';
                unknownCommitCell = '	';
                break;
            case 'FAIL':
                goodCommitCell = '	';
                badCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                unknownCommitCell = '	';
                break;
            default:
                goodCommitCell = '	';
                badCommitCell = '	';
                unknownCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                break;
        }

        tableRows += '
' + gitDescribeCell + badCommitCell +
            unknownCommitCell + goodCommitCell + '
';
    }

    $('#loading-div').remove();
    $('#bad-commit').empty().append(
        '' + badCommit + '');
    if (goodCommit !== null) {
        $('#good-commit').empty().append(
            '' + goodCommit + '');
    } else {
        $('#good-commit').empty().append(
            'No good commit found');
    }

    if (badCommit !== null && goodCommit !== null) {
        $('#dl-bisect-script').removeClass('hidden');
        $('#bisect-script').append(
            '' +
            ''
        );
    } else {
        $('#dl-bisect-script').remove();
    }
    $('#boot-bisect-table-body').empty().append(tableRows);
    $('#table-div').fadeIn("slow", "linear");
}

function bisectAjaxCallFailed(data) {
    'use strict';
    $('#loading-div').remove();
    $('#loading-content').empty().append(
        '
Error loading bisect data from server.
'
    );
}

function getBisectData(data) {
    'use strict';

    var localData = data.result[0],
        status = localData.status,
        docId,
        bisectAjaxCall,
        errorReason;

    errorReason = "Bisect data call failed.";

    if (status === "FAIL") {
        $('#bisect-div').removeClass('hidden');
        docId = boardId + '-' + jobId + '-' + kernelId + '-' + defconfId;

        bisectAjaxCall = $.ajax({
            'url': '/_ajax/bisect/boot/' + docId,
            'traditional': true,
            'cache': true,
            'dataType': 'json',
            'beforeSend': function(jqXHR) {
                setXhrHeader(jqXHR);
            },
            'timeout': 8000,
            'error': function() {
                bisectAjaxCallFailed();
            },
            'statusCode': {
                400: function() {
                    setErrorAlert('bisect-400-error', 400, errorReason);
                },
                404: function() {
                    setErrorAlert('bisect-404-error', 404, errorReason);
                },
                408: function() {
                    errorReason = "Bisect data call failed: timeout".
                    setErrorAlert('bisect-408-error', 408, errorReason);
                },
                500: function() {
                    setErrorAlert('bisect-500-error', 500, errorReason);
                }
            }
        });

        $.when(bisectAjaxCall).done(createBootBisectTable);
    } else {
        $('#bisect-div').remove();
    }
}

$(document).ready(function() {
    'use strict';

    $('body').tooltip({
        'selector': '[rel=tooltip]',
        'placement': 'auto top'
    });

    $('#li-boot').addClass('active');
    $('#table-div').hide();

    var errorReason = 'Data call failed.';

    $.ajax({
        'url': '/_ajax/boot',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'data': {
            'id': boardId + '-' + jobId + '-' + kernelId + '-' + defconfId
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'statusCode': {
            400: function() {
                loadContent(
                    '#container-content', '/static/html/400-content.html'
                );
                setErrorAlert('data-400-error', 400, errorReason);
            },
            404: function() {
                loadContent(
                    '#container-content', '/static/html/404-content.html'
                );
                setErrorAlert('data-404-error', 404, errorReason);
            },
            408: function() {
                loadContent(
                    '#container-content', '/static/html/408-content.html'
                );
                setErrorAlert('data-408-error', 408, errorReason);
            },
            500: function() {
                loadContent(
                    '#container-content', '/static/html/500-content.html'
                );
                setErrorAlert('data-500-error', 500, errorReason);
            }
        }
    }).done(function(data) {
        populatePage(data);
        getBisectData(data);
    });
});
<commit_msg>js: Fix missing space in bisect script generation.<commit_after>var boardId = $('#board-id').val();
var jobId = $('#job-id').val();
var kernelId = $('#kernel-id').val();
var defconfId = $('#defconfig-id').val();

function populatePage(data) {
    'use strict';

    var localData = data.result[0],
        bootTime,
        displ = '',
        fileServer = $('#file-server').val(),
        metadata,
        nonAvail = '' +
            '';

    bootTime = new Date(localData.time['$date']);
    metadata = localData.metadata;

    $('#dd-board-board').empty().append(localData.board);
    $('#dd-board-defconfig').empty().append(
        localData.defconfig + ' — ' +
            ''
    );
    $('#dd-board-kernel').empty().append(
        '' + localData.kernel +
            '' +
            ' — ' +
            ''
    );
    $('#dd-board-tree').empty().append(
        '' +
            '' + localData.job +
            '' +
            ' — ' +
            ''
    );

    if (localData.endian !== null) {
        $('#dd-board-endianness').empty().append(localData.endian);
    } else {
        $('#dd-board-endianness').empty().append(nonAvail);
    }

    if (localData.boot_log !== null || localData.boot_log_html !== null) {
        $('#dd-board-boot-log').empty();

        if (localData.boot_log !== null) {
            $('#dd-board-boot-log').append(
                'txt' +
                ' '
            );
        }

        if (localData.boot_log_html !== null) {
            if (localData.boot_log !== null) {
                $('#dd-board-boot-log').append(' — ');
            }
            $('#dd-board-boot-log').append(
                'html '
            );
        }
    } else {
        $('#dd-board-boot-log').empty().append(nonAvail);
    }

    switch (localData.status) {
        case 'PASS':
            displ = '';
            break;
        case 'FAIL':
            displ = '' +
                    '';
            break;
        case 'OFFLINE':
            displ = '<span rel="tooltip" data-toggle="tooltip"' +
                'title="Board offline" ' +
                '';
            break;
        default:
            displ = '' +
                    '';
            break;
    }

    // Do we have a description for the boot result?
    // We might have it directly in the json or in the metadata property.
    if (localData.hasOwnProperty('boot_result_description')) {
        displ += ' ' + localData.boot_result_description +
            '';
    } else if (! $.isEmptyObject(metadata) &&
            metadata.hasOwnProperty('boot_result_description')) {
        displ += ' ' + metadata.boot_result_description +
            '';
    }

    $('#dd-board-status').empty().append(displ);
    $('#dd-board-boot-time').empty().append(bootTime.getCustomTime());

    if (localData.warnings !== null) {
        $('#dd-board-warnings').empty().append(localData.warnings);
    } else {
        $('#dd-board-warnings').empty().append(0);
    }

    if (localData.dtb !== null && localData.dtb !== '') {
        $('#dd-board-dtb').empty().append(localData.dtb);
    } else {
        $('#dd-board-dtb').empty().append(nonAvail);
    }

    if (localData.dtb_addr !== null && localData.dtb_addr !== '') {
        $('#dd-board-dtb-address').empty().append(localData.dtb_addr);
    } else {
        $('#dd-board-dtb-address').empty().append(nonAvail);
    }

    if (localData.initrd_addr !== null && localData.initrd_addr !== '') {
        $('#dd-board-initrd-address').empty().append(localData.initrd_addr);
    } else {
        $('#dd-board-initrd-address').empty().append(nonAvail);
    }

    if (localData.load_addr !== null && localData.load_addr !== '') {
        $('#dd-board-load-address').empty().append(localData.load_addr);
    } else {
        $('#dd-board-load-address').empty().append(nonAvail);
    }

    if (localData.kernel_image !== null && localData.kernel_image !== '') {
        $('#dd-board-kernel-image').empty().append(localData.kernel_image);
    } else {
        $('#dd-board-kernel-image').empty().append(nonAvail);
    }
}

function createBisectScriptURI(badCommit, goodCommit) {
    'use strict';
    var bisectScript = '#!/bin/bash\n' +
        'git bisect start ' + badCommit + ' ' + goodCommit + '\n';

    return 'data:text/plain;charset=UTF-8,' + encodeURIComponent(bisectScript);
}

function createBootBisectTable(data) {
    'use strict';
    $('#loading-content').empty().append("loading bisect data…");

    var localData = data.result,
        localLen = localData.length,
        i = 0,
        bisectData,
        gitDescribeCell,
        badCommitCell,
        unknownCommitCell,
        goodCommitCell,
        bootStatus,
        bisectDefMetadata,
        tableRows,
        tooltipLink,
        tooltipTitle,
        gitURLs,
        gitDescribeVal,
        badCommit = null,
        goodCommit = null;

    badCommit = localData[0].defconfig_metadata.git_commit;

    for (i; i < localLen; i++) {
        bisectData = localData[i];
        bootStatus = bisectData.boot_status;
        bisectDefMetadata = bisectData.defconfig_metadata;
        gitDescribeVal = bisectDefMetadata.git_describe;

        tooltipLink = '' +
            gitDescribeVal + '';

        tooltipTitle = 'Boot report details for ' + jobId +
            ' ‐ ' + gitDescribeVal;

        gitDescribeCell = '' +
            '' +
            '' + tooltipLink +
            '	';

        gitURLs = translateCommitURL(
            bisectDefMetadata.git_url, bisectDefMetadata.git_commit);

        switch (bootStatus) {
            case 'PASS':
                goodCommit = bisectDefMetadata.git_commit;
                goodCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                badCommitCell = '	';
                unknownCommitCell = '	';
                break;
            case 'FAIL':
                goodCommitCell = '	';
                badCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                unknownCommitCell = '	';
                break;
            default:
                goodCommitCell = '	';
                badCommitCell = '	';
                unknownCommitCell = '' + bisectDefMetadata.git_commit +
                    ' 	';
                break;
        }

        tableRows += '
' + gitDescribeCell + badCommitCell +
            unknownCommitCell + goodCommitCell + '
';
    }

    $('#loading-div').remove();
    $('#bad-commit').empty().append(
        '' + badCommit + '');
    if (goodCommit !== null) {
        $('#good-commit').empty().append(
            '' + goodCommit + '');
    } else {
        $('#good-commit').empty().append(
            'No good commit found');
    }

    if (badCommit !== null && goodCommit !== null) {
        $('#dl-bisect-script').removeClass('hidden');
        $('#bisect-script').append(
            '' +
            ''
        );
    } else {
        $('#dl-bisect-script').remove();
    }
    $('#boot-bisect-table-body').empty().append(tableRows);
    $('#table-div').fadeIn("slow", "linear");
}

function bisectAjaxCallFailed(data) {
    'use strict';
    $('#loading-div').remove();
    $('#loading-content').empty().append(
        '
Error loading bisect data from server.
'
    );
}

function getBisectData(data) {
    'use strict';

    var localData = data.result[0],
        status = localData.status,
        docId,
        bisectAjaxCall,
        errorReason;

    errorReason = "Bisect data call failed.";

    if (status === "FAIL") {
        $('#bisect-div').removeClass('hidden');
        docId = boardId + '-' + jobId + '-' + kernelId + '-' + defconfId;

        bisectAjaxCall = $.ajax({
            'url': '/_ajax/bisect/boot/' + docId,
            'traditional': true,
            'cache': true,
            'dataType': 'json',
            'beforeSend': function(jqXHR) {
                setXhrHeader(jqXHR);
            },
            'timeout': 8000,
            'error': function() {
                bisectAjaxCallFailed();
            },
            'statusCode': {
                400: function() {
                    setErrorAlert('bisect-400-error', 400, errorReason);
                },
                404: function() {
                    setErrorAlert('bisect-404-error', 404, errorReason);
                },
                408: function() {
                    errorReason = "Bisect data call failed: timeout".
                    setErrorAlert('bisect-408-error', 408, errorReason);
                },
                500: function() {
                    setErrorAlert('bisect-500-error', 500, errorReason);
                }
            }
        });

        $.when(bisectAjaxCall).done(createBootBisectTable);
    } else {
        $('#bisect-div').remove();
    }
}

$(document).ready(function() {
    'use strict';

    $('body').tooltip({
        'selector': '[rel=tooltip]',
        'placement': 'auto top'
    });

    $('#li-boot').addClass('active');
    $('#table-div').hide();

    var errorReason = 'Data call failed.';

    $.ajax({
        'url': '/_ajax/boot',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'data': {
            'id': boardId + '-' + jobId + '-' + kernelId + '-' + defconfId
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'statusCode': {
            400: function() {
                loadContent(
                    '#container-content', '/static/html/400-content.html'
                );
                setErrorAlert('data-400-error', 400, errorReason);
            },
            404: function() {
                loadContent(
                    '#container-content', '/static/html/404-content.html'
                );
                setErrorAlert('data-404-error', 404, errorReason);
            },
            408: function() {
                loadContent(
                    '#container-content', '/static/html/408-content.html'
                );
                setErrorAlert('data-408-error', 408, errorReason);
            },
            500: function() {
                loadContent(
                    '#container-content', '/static/html/500-content.html'
                );
                setErrorAlert('data-500-error', 500, errorReason);
            }
        }
    }).done(function(data) {
        populatePage(data);
        getBisectData(data);
    });
});
<|endoftext|>


<commit_before>/**
 * @providesModule edgenotesBySlug
 * @flow
 */

import produce from 'immer'

import type { EdgenotesState } from 'redux/state'
import type {
  AddEdgenoteAction,
  UpdateEdgenoteAction,
  RemoveEdgenoteAction,
} from 'redux/actions'

export default function edgenotesBySlug (
  state: EdgenotesState = ({ ...window.caseData.edgenotes }: EdgenotesState),
  action: AddEdgenoteAction | UpdateEdgenoteAction | RemoveEdgenoteAction
): EdgenotesState {
  switch (action.type) {
    case 'ADD_EDGENOTE':
      return {
        ...state,
        [action.slug]: action.data,
      }

    case 'UPDATE_EDGENOTE':
      return {
        ...state,
        [action.slug]: {
          ...state[action.slug],
          ...action.data,
        },
      }

    case 'REMOVE_EDGENOTE':
      return produce(state, state => {
        delete state[action.slug]
      })

    default:
      return state
  }
}
<commit_msg>fix: dasherize edgenotesBySlug keys on arrival<commit_after>/**
 * @providesModule edgenotesBySlug
 * @flow
 */

import * as R from 'ramda'
import produce from 'immer'
import { paramCase } from 'change-case'

import type { EdgenotesState } from 'redux/state'
import type {
  AddEdgenoteAction,
  UpdateEdgenoteAction,
  RemoveEdgenoteAction,
} from 'redux/actions'

export default function edgenotesBySlug (
  state: EdgenotesState = ({
    ...dasherizeKeys(window.caseData.edgenotes),
  }: EdgenotesState),
  action: AddEdgenoteAction | UpdateEdgenoteAction | RemoveEdgenoteAction
): EdgenotesState {
  switch (action.type) {
    case 'ADD_EDGENOTE':
      return {
        ...state,
        [action.slug]: action.data,
      }

    case 'UPDATE_EDGENOTE':
      return {
        ...state,
        [action.slug]: {
          ...state[action.slug],
          ...action.data,
        },
      }

    case 'REMOVE_EDGENOTE':
      return produce(state, state => {
        delete state[action.slug]
      })

    default:
      return state
  }
}

const renameBy = R.curry((fn, obj) =>
  R.pipe(R.toPairs, R.map(R.adjust(fn, 0)), R.fromPairs)(obj)
)
const dasherizeKeys = renameBy(paramCase)
<|endoftext|>


<commit_before>import React from 'react'
import sinon from 'sinon'
import { expect } from 'chai'
import { render, mount, shallow } from 'enzyme'
import { DraftWidget } from './../../components'
import * as Paths from './../../Paths'

let wrapper

describe('DraftWidget', () => {
  let props = {
    dispatch     : () => {},
    auth         : {},
    mobilization : {},
    widget       : {},
    editable     : true
  }

  context('when it is editable', () => {
    before(() => {
      sinon.spy(DraftWidget.prototype, 'updateKind')
    })

    beforeEach(() => {
      wrapper = shallow(<DraftWidget {...props} />)
    })

    after(() => {
      DraftWidget.prototype.updateKind.restore()
    })

    describe('#render', () => {
      it('should render a draft widget containing html elements passing editable prop enabled', () => {
        props.editable = true

        expect(wrapper.find('div.widget').length).to.equal(1)
        expect(wrapper.find('div.widget h4').length).to.equal(1)
        expect(wrapper.find('div.widget button').length).to.equal(3)
        expect(wrapper.state().loading).to.equal(false)
      })
    })

    describe('#handleContentClick', () => {
      it('should call updateKind with `content` parameter', () => {
        wrapper.find('button').at(0).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('content')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })

    describe('#handleFormClick', () => {
      it('should call updateKind with `form` parameter', () => {
        wrapper.find('button').at(1).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('form')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })

    describe('#handleDonationClick', () => {
      it('should call updateKind with `donation` parameter', () => {
        wrapper.find('button').at(2).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('donation')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })
  })
})
<commit_msg>Add Loading component render test.<commit_after>import React from 'react'
import sinon from 'sinon'
import { expect } from 'chai'
import { render, mount, shallow } from 'enzyme'
import { DraftWidget, Loading } from './../../components'
import * as Paths from './../../Paths'

let wrapper

describe('DraftWidget', () => {
  let props = {
    dispatch     : () => {},
    auth         : {},
    mobilization : {},
    widget       : {},
    editable     : true
  }

  context('when it is editable', () => {
    before(() => {
      sinon.spy(DraftWidget.prototype, 'updateKind')
    })

    beforeEach(() => {
      wrapper = shallow(<DraftWidget {...props} />)
    })

    after(() => {
      DraftWidget.prototype.updateKind.restore()
    })

    describe('#render', () => {
      it('should render a draft widget containing html elements passing editable prop enabled', () => {
        expect(wrapper.find('div.widget').length).to.equal(1)
        expect(wrapper.find('div.widget h4').length).to.equal(1)
        expect(wrapper.find('div.widget button').length).to.equal(3)
        expect(wrapper.state().loading).to.equal(false)
      })
    })

    describe('#handleContentClick', () => {
      it('should call updateKind with `content` parameter', () => {
        wrapper.find('button').at(0).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('content')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })

    describe('#handleFormClick', () => {
      it('should call updateKind with `form` parameter', () => {
        wrapper.find('button').at(1).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('form')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })

    describe('#handleDonationClick', () => {
      it('should call updateKind with `donation` parameter', () => {
        wrapper.find('button').at(2).simulate('click', { preventDefault() {} })

        const updateKind = DraftWidget.prototype.updateKind
        expect(updateKind.called).to.equal(true)
        expect(updateKind.calledWith('donation')).to.equal(true)
        expect(wrapper.state().loading).to.equal(true)
      })
    })

    describe('#renderLoading', () => {
      it('should return rendered Loading component if `state.loading` is true', () => {
        wrapper.setState({ loading: true })
        expect(wrapper.contains(<Loading />)).to.be.true
      })
    })
  })

  context('when it isn\'t editable', () => {
    before(() => {
      wrapper = shallow(<DraftWidget {...props} editable={false} />)
    })

    describe('#render', () => {
      it('should render an empty div', () => {
        expect(wrapper.contains(

)).to.be.true
        expect(wrapper.find('div.widget').length).to.equal(0)
        expect(wrapper.find('div.widget h4').length).to.equal(0)
        expect(wrapper.find('div.widget button').length).to.equal(0)
      })
    })
  })
})
<|endoftext|>


<commit_before>import React from 'react';
import {
	View,
	Text,
	ListView,
	StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
import moment from 'moment';

import {
	MAX_CARD_WIDTH,
	WINDOW_WIDTH,
	WINDOW_HEIGHT,
	NAVIGATOR_HEIGHT,
	TAB_BAR_HEIGHT,
} from '../../styles/LayoutConstants';
import EmptyItem from './EmptyItem';
import SpecialEventsItem from './SpecialEventsItem';
import SpecialEventsHeader from './SpecialEventsHeader';

const dataSource = new ListView.DataSource({
	rowHasChanged: (r1, r2) => r1 !== r2,
	sectionHeaderHasChanged: (s1, s2) => s1 !== s2
});

const SpecialEventsListView = ({ addSpecialEvents, specialEventsSchedule,
	specialEventsScheduleIds, removeSpecialEvents, saved,disabled, personal, rows,
	scrollEnabled, style, labels, labelItemIds, selectedDay, days, daysItemIds, inCard }) => {

	let scheduleIdArray = [];
	// Use ids from selectedDay
	if (daysItemIds) {
		if (!selectedDay) {
			// Select current day by default
			for (let i = 0; i < days.length; ++i) {
				selectedDay = days[i];
				if (moment(selectedDay).isSameOrAfter(moment(), 'day')) {
					scheduleIdArray = daysItemIds[selectedDay];

					// Filter saved for day
					if (personal && Array.isArray(saved)) {
						scheduleIdArray = scheduleIdArray.filter((item) => saved.includes(item));
					}

					// If displaying for card...continue looking for a day with saved
					if (inCard && scheduleIdArray.length === 0) {
						// continue
					} else {
						break;
					}
				}
			}
		}
		scheduleIdArray = daysItemIds[selectedDay];

		// Filter saved for day
		if (personal && Array.isArray(saved)) {
			scheduleIdArray = scheduleIdArray.filter((item) => saved.includes(item));
		}

		// Apply label filtering
		if (!personal && labels.length > 0) {
			let labelArray = [];
			for (let j = 0; j < labels.length; ++j) {
				const label = labels[j];
				const items = labelItemIds[label];

				labelArray = labelArray.concat(items);
			}
			scheduleIdArray = scheduleIdArray.filter((item) => labelArray.includes(item));
		}
	}
	if (personal && scheduleIdArray.length === 0) {
		return (
			<View style={[style, rows ? styles.card : styles.full]}>
				<Text style={styles.noSavedSessions}>
					Click the star icon next to a session to save it to your list.
				</Text>
			</View>
		);
	} else {
		return (
			<ListView
				style={[style, rows ? styles.card : styles.full]}
				scrollEnabled={scrollEnabled}
				stickySectionHeadersEnabled={false}
				dataSource={
					dataSource.cloneWithRowsAndSections(
						convertToTimeMap(
							specialEventsSchedule,
							adjustData(specialEventsSchedule, scheduleIdArray, saved, personal, rows)
						)
					)
				}
				renderRow={(rowData, sectionID, rowID, highlightRow) => {
					// Don't render first row bc rendered by header
					if (Number(rowID) !== 0) {
						return (
							<View style={styles.rowContainer}>
								<EmptyItem />
								<SpecialEventsItem
									specialEventsData={rowData}
									saved={saved.includes(rowData.id)}
									add={(disabled) ? null : addSpecialEvents}
									remove={removeSpecialEvents}
								/>
							</View>
						);
					} else {
						return null;
					}
				}}
				renderSectionHeader={(sectionData, sectionID) => (
					// Render header along with first row
					<View style={styles.rowContainer}>
						<SpecialEventsHeader
							timestamp={sectionID}
						/>
						<SpecialEventsItem
							specialEventsData={sectionData[0]}
							saved={saved.includes(sectionData[0].id)}
							add={(disabled) ? null : addSpecialEvents}
							remove={removeSpecialEvents}
						/>
					</View>
				)}
			/>
		);
	}
};

/*
	Filters what session ids to use based on personal/saved and/or rows
	@returns Array of session ids
 */
function adjustData(scheduleIdMap, scheduleIdArray, savedArray, personal, rows) {
	// Filter out saved items
	if (!personal || !Array.isArray(savedArray)) {
		const keys = scheduleIdArray;
		if (!rows) {
			return scheduleIdArray;
		} else {
			const trimmed = [];

			for (let i = 0; i < rows; ++i) {
				trimmed.push(keys[i]);
			}
			return trimmed;
		}
	} else {
		let filtered = [];
		// Check if saved item is part of ids to be displayed
		for (let i = 0; i < savedArray.length; ++i) {
			const key = savedArray[i];
			if (scheduleIdArray.includes(key)) {
				filtered.push(key);
			}
		}

		// Displaying for homecard
		// Remove sessions that have passed
		if (rows) {
			const now = Date.now();
			if (filtered.length > rows) {
				const temp = filtered.slice();
				for (let j = 0; j < filtered.length; ++j) {
					const key = filtered[j];
					const itemTime = scheduleIdMap[key]['start-time'];

					if (now > itemTime) {
						const index = temp.indexOf(key);
						temp.splice(index, 1);
					}

					if (temp.length <= rows) {
						break;
					}
				}

				if (temp.length > rows) {
					filtered = temp.slice(0, rows);
				} else {
					filtered = temp;
				}
			}
		}
		return filtered;
	}
}

/*
	@returns Object that maps keys from scheduleArray to Objects in scheduleMap
*/
function convertToTimeMap(scheduleMap, scheduleArray, header = false) {
	const timeMap = {};

	if (Array.isArray(scheduleArray)) {
		scheduleArray.forEach((key) => {
			const session = scheduleMap[key];
			if (!timeMap[session['start-time']]) {
				// Create an entry in the map for the timestamp if it hasn't yet been created
				timeMap[session['start-time']] = [];
			}
			timeMap[session['start-time']].push(session);
		});

		// Remove an item from section so spacing lines up
		if (header) {
			Object.keys(timeMap).forEach((key) => {
				timeMap[key].pop();
			});
		}
	}
	return timeMap;
}

const mapStateToProps = (state) => (
	{
		specialEventsSchedule: state.specialEvents.data.schedule,
		specialEventsScheduleIds: state.specialEvents.data.uids,
		saved: state.specialEvents.saved,
		labelItemIds: state.specialEvents.data['label-items'],
		labels: state.specialEvents.labels,
		days: state.specialEvents.data.dates,
		daysItemIds: state.specialEvents.data['date-items'],
	}
);

const mapDispatchToProps = (dispatch) => (
	{
		addSpecialEvents: (id) => {
			dispatch({ type: 'ADD_SPECIAL_EVENTS', id });
		},
		removeSpecialEvents: (id) => {
			dispatch({ type: 'REMOVE_SPECIAL_EVENTS', id });
		}
	}
);

const ActualSpecialEventsListView = connect(
	mapStateToProps,
	mapDispatchToProps
)(SpecialEventsListView);

const styles = StyleSheet.create({
	rowContainer: { flexDirection: 'row', height: 76 },
	full: { width: WINDOW_WIDTH, height: (WINDOW_HEIGHT - NAVIGATOR_HEIGHT - TAB_BAR_HEIGHT) },
	card: { width: MAX_CARD_WIDTH },
	noSavedSessions: { flexGrow: 1, fontSize: 18, textAlign: 'center', padding: 40, lineHeight: 30 },
});

export default ActualSpecialEventsListView;
<commit_msg>Change list to schedule<commit_after>import React from 'react';
import {
	View,
	Text,
	ListView,
	StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
import moment from 'moment';

import {
	MAX_CARD_WIDTH,
	WINDOW_WIDTH,
	WINDOW_HEIGHT,
	NAVIGATOR_HEIGHT,
	TAB_BAR_HEIGHT,
} from '../../styles/LayoutConstants';
import EmptyItem from './EmptyItem';
import SpecialEventsItem from './SpecialEventsItem';
import SpecialEventsHeader from './SpecialEventsHeader';

const dataSource = new ListView.DataSource({
	rowHasChanged: (r1, r2) => r1 !== r2,
	sectionHeaderHasChanged: (s1, s2) => s1 !== s2
});

const SpecialEventsListView = ({ addSpecialEvents, specialEventsSchedule,
	specialEventsScheduleIds, removeSpecialEvents, saved,disabled, personal, rows,
	scrollEnabled, style, labels, labelItemIds, selectedDay, days, daysItemIds, inCard }) => {

	let scheduleIdArray = [];
	// Use ids from selectedDay
	if (daysItemIds) {
		if (!selectedDay) {
			// Select current day by default
			for (let i = 0; i < days.length; ++i) {
				selectedDay = days[i];
				if (moment(selectedDay).isSameOrAfter(moment(), 'day')) {
					scheduleIdArray = daysItemIds[selectedDay];

					// Filter saved for day
					if (personal && Array.isArray(saved)) {
						scheduleIdArray = scheduleIdArray.filter((item) => saved.includes(item));
					}

					// If displaying for card...continue looking for a day with saved
					if (inCard && scheduleIdArray.length === 0) {
						// continue
					} else {
						break;
					}
				}
			}
		}
		scheduleIdArray = daysItemIds[selectedDay];

		// Filter saved for day
		if (personal && Array.isArray(saved)) {
			scheduleIdArray = scheduleIdArray.filter((item) => saved.includes(item));
		}

		// Apply label filtering
		if (!personal && labels.length > 0) {
			let labelArray = [];
			for (let j = 0; j < labels.length; ++j) {
				const label = labels[j];
				const items = labelItemIds[label];

				labelArray = labelArray.concat(items);
			}
			scheduleIdArray = scheduleIdArray.filter((item) => labelArray.includes(item));
		}
	}
	if (personal && scheduleIdArray.length === 0) {
		return (
			<View style={[style, rows ? styles.card : styles.full]}>
				<Text style={styles.noSavedSessions}>
					Click the star icon next to a session to save it to your schedule.
				</Text>
			</View>
		);
	} else {
		return (
			<ListView
				style={[style, rows ? styles.card : styles.full]}
				scrollEnabled={scrollEnabled}
				stickySectionHeadersEnabled={false}
				dataSource={
					dataSource.cloneWithRowsAndSections(
						convertToTimeMap(
							specialEventsSchedule,
							adjustData(specialEventsSchedule, scheduleIdArray, saved, personal, rows)
						)
					)
				}
				renderRow={(rowData, sectionID, rowID, highlightRow) => {
					// Don't render first row bc rendered by header
					if (Number(rowID) !== 0) {
						return (
							<View style={styles.rowContainer}>
								<EmptyItem />
								<SpecialEventsItem
									specialEventsData={rowData}
									saved={saved.includes(rowData.id)}
									add={(disabled) ? null : addSpecialEvents}
									remove={removeSpecialEvents}
								/>
							</View>
						);
					} else {
						return null;
					}
				}}
				renderSectionHeader={(sectionData, sectionID) => (
					// Render header along with first row
					<View style={styles.rowContainer}>
						<SpecialEventsHeader
							timestamp={sectionID}
						/>
						<SpecialEventsItem
							specialEventsData={sectionData[0]}
							saved={saved.includes(sectionData[0].id)}
							add={(disabled) ? null : addSpecialEvents}
							remove={removeSpecialEvents}
						/>
					</View>
				)}
			/>
		);
	}
};

/*
	Filters what session ids to use based on personal/saved and/or rows
	@returns Array of session ids
 */
function adjustData(scheduleIdMap, scheduleIdArray, savedArray, personal, rows) {
	// Filter out saved items
	if (!personal || !Array.isArray(savedArray)) {
		const keys = scheduleIdArray;
		if (!rows) {
			return scheduleIdArray;
		} else {
			const trimmed = [];

			for (let i = 0; i < rows; ++i) {
				trimmed.push(keys[i]);
			}
			return trimmed;
		}
	} else {
		let filtered = [];
		// Check if saved item is part of ids to be displayed
		for (let i = 0; i < savedArray.length; ++i) {
			const key = savedArray[i];
			if (scheduleIdArray.includes(key)) {
				filtered.push(key);
			}
		}

		// Displaying for homecard
		// Remove sessions that have passed
		if (rows) {
			const now = Date.now();
			if (filtered.length > rows) {
				const temp = filtered.slice();
				for (let j = 0; j < filtered.length; ++j) {
					const key = filtered[j];
					const itemTime = scheduleIdMap[key]['start-time'];

					if (now > itemTime) {
						const index = temp.indexOf(key);
						temp.splice(index, 1);
					}

					if (temp.length <= rows) {
						break;
					}
				}

				if (temp.length > rows) {
					filtered = temp.slice(0, rows);
				} else {
					filtered = temp;
				}
			}
		}
		return filtered;
	}
}

/*
	@returns Object that maps keys from scheduleArray to Objects in scheduleMap
*/
function convertToTimeMap(scheduleMap, scheduleArray, header = false) {
	const timeMap = {};

	if (Array.isArray(scheduleArray)) {
		scheduleArray.forEach((key) => {
			const session = scheduleMap[key];
			if (!timeMap[session['start-time']]) {
				// Create an entry in the map for the timestamp if it hasn't yet been created
				timeMap[session['start-time']] = [];
			}
			timeMap[session['start-time']].push(session);
		});

		// Remove an item from section so spacing lines up
		if (header) {
			Object.keys(timeMap).forEach((key) => {
				timeMap[key].pop();
			});
		}
	}
	return timeMap;
}

const mapStateToProps = (state) => (
	{
		specialEventsSchedule: state.specialEvents.data.schedule,
		specialEventsScheduleIds: state.specialEvents.data.uids,
		saved: state.specialEvents.saved,
		labelItemIds: state.specialEvents.data['label-items'],
		labels: state.specialEvents.labels,
		days: state.specialEvents.data.dates,
		daysItemIds: state.specialEvents.data['date-items'],
	}
);

const mapDispatchToProps = (dispatch) => (
	{
		addSpecialEvents: (id) => {
			dispatch({ type: 'ADD_SPECIAL_EVENTS', id });
		},
		removeSpecialEvents: (id) => {
			dispatch({ type: 'REMOVE_SPECIAL_EVENTS', id });
		}
	}
);

const ActualSpecialEventsListView = connect(
	mapStateToProps,
	mapDispatchToProps
)(SpecialEventsListView);

const styles = StyleSheet.create({
	rowContainer: { flexDirection: 'row', height: 76 },
	full: { width: WINDOW_WIDTH, height: (WINDOW_HEIGHT - NAVIGATOR_HEIGHT - TAB_BAR_HEIGHT) },
	card: { width: MAX_CARD_WIDTH },
	noSavedSessions: { flexGrow: 1, fontSize: 18, textAlign: 'center', padding: 40, lineHeight: 30 },
});

export default ActualSpecialEventsListView;
<|endoftext|>


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

/**
 * @fileoverview Angular2 Service that manages all game logic.
 * @author sll@google.com (Sean Lip)
 */

musicGame.NAME = 'MUSIC_ACCESSIBLE';

musicGame.LevelManagerService = ng.core.Class({
  constructor: [
    musicGame.UtilsService, musicGame.GenericModalService,
    function(utilsService, genericModalService) {
      this.genericModalService = genericModalService;

      this.levelSetId_ = utilsService.getStringParamFromUrl(
          'levelset', 'tutorial');
      this.levelSet_ = LEVEL_SETS[this.levelSetId_].levels;

      // Level numbers here are 0-indexed.
      var levelNumberFromUrl = utilsService.getStringParamFromUrl('l', '1');
      this.currentLevelNumber_ = levelNumberFromUrl - 1;

      this.applauseAudioFile = new Audio('media/applause.mp3');
      this.oopsAudioFile = new Audio('media/oops.mp3');
    }
  ],
  loadExistingCode: function() {
    var inherit = this.getCurrentLevelData().continueFromPreviousLevel;
    var defaultXmlText = this.getCurrentLevelData().defaultXml;

    // Try loading saved XML for this level, saved XML from previous level,
    // or default XML for this level, in that order.
    var xmlTextToRestore = this.loadCodeFromLocalStorage_(
        this.levelSetId_, this.currentLevelNumber_);
    if (!xmlTextToRestore && inherit) {
      var inheritedXmlText = this.loadCodeFromLocalStorage_(
          this.levelSetId_, this.currentLevelNumber_ - 1);
      if (inheritedXmlText) {
        xmlTextToRestore = inheritedXmlText;
      }
    }
    if (!xmlTextToRestore && defaultXmlText) {
      xmlTextToRestore = defaultXmlText;
    }

    if (xmlTextToRestore) {
      // The timeout is needed to give the workspace a chance to load before
      // the initial XML is converted to blocks.
      setTimeout(function() {
        var xml = Blockly.Xml.textToDom(xmlTextToRestore);
        Blockly.Xml.domToWorkspace(xml, blocklyApp.workspace);
      }, 0);
    }
  },
  playApplauseSound: function() {
    this.applauseAudioFile.play();
  },
  playOopsSound: function() {
    this.oopsAudioFile.play();
  },
  getLocalStorageCodeKey_: function(levelSetId, levelNumber) {
    return [musicGame.NAME, levelSetId, levelNumber].join('.');
  },
  clearData: function() {
    if (!confirm('Delete all your solutions?')) {
      return;
    }

    var that = this;
    this.getLevelSetsMetadata().forEach(function(levelSetMetadata) {
      levelSetMetadata.levels.forEach(function(level) {
        var key = that.getLocalStorageCodeKey_(
            levelSetMetadata.id, level.number1Indexed - 1);
        delete window.localStorage[key];
      });
    });

    location.reload();
  },
  saveCodeToLocalStorage_: function(levelSetId, levelNumber) {
    // MSIE 11 does not support localStorage on file:// URLs.
    if (!window.localStorage) {
      return;
    }
    var key = this.getLocalStorageCodeKey_(levelSetId, levelNumber);
    var xml = Blockly.Xml.workspaceToDom(blocklyApp.workspace);
    window.localStorage[key] = Blockly.Xml.domToText(xml);
  },
  loadCodeFromLocalStorage_: function(levelSetId, levelNumber) {
    if (!window.localStorage) {
      return;
    }

    var key = this.getLocalStorageCodeKey_(levelSetId, levelNumber);
    return window.localStorage[key];
  },
  getLevelSetId: function() {
    return this.levelSetId_;
  },
  getLevelSetName: function() {
    return LEVEL_SETS[this.levelSetId_].name;
  },
  getLevelSetsMetadata: function() {
    if (!this.levelSetsMetadata) {
      this.levelSetsMetadata = [];

      var that = this;
      for (var levelSetId in LEVEL_SETS) {
        var levels = LEVEL_SETS[levelSetId].levels.map(function(_, index) {
          return {
            number1Indexed: index + 1,
            completed: that.loadCodeFromLocalStorage_(levelSetId, index)
          };
        });

        this.levelSetsMetadata.push({
          id: levelSetId,
          name: LEVEL_SETS[levelSetId].name,
          levels: levels
        });
      }
    }

    return this.levelSetsMetadata;
  },
  getCurrentLevelNumber: function() {
    return this.currentLevelNumber_;
  },
  getNumberOfLevels: function() {
    return this.levelSet_.length;
  },
  getToolboxBlockDefns: function() {
    return this.getCurrentLevelData().toolboxBlockDefns;
  },
  setCurrentLevel: function(current1IndexedLevelNumber) {
    this.currentLevelNumber_ = current1IndexedLevelNumber - 1;
  },
  getCurrentLevelData: function() {
    return this.levelSet_[this.currentLevelNumber_];
  },
  showModal_: function(
      header, messageParagraphs, actionButtonsInfo, onDismissCallback) {
    this.genericModalService.showModal(
        header, messageParagraphs, actionButtonsInfo, onDismissCallback);
  },
  showSimpleModalWithHeader: function(
      header, messageParagraphs, onDismissCallback) {
    this.showModal_(header, messageParagraphs, [], onDismissCallback);
  },
  showSimpleModal: function(messageParagraphs, onDismissCallback) {
    this.showModal_('', messageParagraphs, [], onDismissCallback);
  },
  showInstructions: function() {
    var levelData = this.getCurrentLevelData();

    var messageParagraphs = [levelData.instructions];
    if (levelData.hint) {
      messageParagraphs.push('Hint: ' + levelData.hint);
    }

    var actionButtonsInfo = [];
    if (levelData.expectedLine) {
      actionButtonsInfo.push({
        text: 'Listen to example',
        action: function() {
          var expectedLine = new MusicLine();
          expectedLine.setFromChordsAndDurations(levelData.expectedLine);

          musicPlayer.reset();
          musicPlayer.play(expectedLine, levelData.beatsPerMinute);
        }
      });
    }

    this.showModal_(
        'Instructions', messageParagraphs, actionButtonsInfo,
        function() {
          document.getElementById('musicGameInstructionsBtn').focus();
        });
  },
  gradeCurrentLevel: function() {
    var currentLevelData = this.getCurrentLevelData();
    var expectedPlayerLine = new MusicLine();
    expectedPlayerLine.setFromChordsAndDurations(
        currentLevelData.expectedLine);

    var correct = musicPlayer.doesPlayerLineEqual(expectedPlayerLine);
    if (correct) {
      this.saveCodeToLocalStorage_(
          this.getLevelSetId(), this.getCurrentLevelNumber());
      this.playApplauseSound();

      if (this.currentLevelNumber_ == this.levelSet_.length - 1) {
        if (this.levelSetId_ == 'tutorial') {
          var messageParagraphs = [
            'Congratulations, you have finished the tutorial levels!',
            'Press Enter to continue to the main game.'];
          this.showSimpleModal(messageParagraphs, function() {
            window.location =
                window.location.protocol + '//' +
                window.location.host + window.location.pathname +
                '?l=1&levelset=game1';
            }
          );
        }
      } else {
        var levelSetId = this.levelSetId_;
        var nextLevelNumber1Indexed = Number(this.currentLevelNumber_ + 2);
        var messageParagraphs = [
          'Good job! You completed the level!',
          'Press Enter to continue to level ' + nextLevelNumber1Indexed + '.'];
        this.showSimpleModal(messageParagraphs, function() {
          window.location =
              window.location.protocol + '//' +
              window.location.host + window.location.pathname +
              '?l=' + nextLevelNumber1Indexed + '&levelset=' + levelSetId;
        });
      }
    } else {
      var playerChords = musicPlayer.getPlayerChords();
      var errorMessage = (
          'Are you playing the right notes? Compare your tune and the ' +
          'desired tune to see if there\'s a difference. Try again!');
      if (currentLevelData.getTargetedFeedback) {
        var targetedMessage = currentLevelData.getTargetedFeedback(
            playerChords);
        if (targetedMessage) {
          errorMessage = targetedMessage;
        }
      }
      this.showSimpleModal([errorMessage], function() {
        document.getElementById('musicGameRunProgramBtn').focus();
      });
    }
  }
});
<commit_msg>Focus on the workspace after the instructions dialog is closed, if there are currently no blocks in it.<commit_after>/**
 * Blockly Games: Accessible
 *
 * Copyright 2016 Google Inc.
 * https://github.com/google/blockly-games
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Angular2 Service that manages all game logic.
 * @author sll@google.com (Sean Lip)
 */

musicGame.NAME = 'MUSIC_ACCESSIBLE';

musicGame.LevelManagerService = ng.core.Class({
  constructor: [
    musicGame.UtilsService, musicGame.GenericModalService,
    function(utilsService, genericModalService) {
      this.genericModalService = genericModalService;

      this.levelSetId_ = utilsService.getStringParamFromUrl(
          'levelset', 'tutorial');
      this.levelSet_ = LEVEL_SETS[this.levelSetId_].levels;

      // Level numbers here are 0-indexed.
      var levelNumberFromUrl = utilsService.getStringParamFromUrl('l', '1');
      this.currentLevelNumber_ = levelNumberFromUrl - 1;

      this.applauseAudioFile = new Audio('media/applause.mp3');
      this.oopsAudioFile = new Audio('media/oops.mp3');
    }
  ],
  loadExistingCode: function() {
    var inherit = this.getCurrentLevelData().continueFromPreviousLevel;
    var defaultXmlText = this.getCurrentLevelData().defaultXml;

    // Try loading saved XML for this level, saved XML from previous level,
    // or default XML for this level, in that order.
    var xmlTextToRestore = this.loadCodeFromLocalStorage_(
        this.levelSetId_, this.currentLevelNumber_);
    if (!xmlTextToRestore && inherit) {
      var inheritedXmlText = this.loadCodeFromLocalStorage_(
          this.levelSetId_, this.currentLevelNumber_ - 1);
      if (inheritedXmlText) {
        xmlTextToRestore = inheritedXmlText;
      }
    }
    if (!xmlTextToRestore && defaultXmlText) {
      xmlTextToRestore = defaultXmlText;
    }

    if (xmlTextToRestore) {
      // The timeout is needed to give the workspace a chance to load before
      // the initial XML is converted to blocks.
      setTimeout(function() {
        var xml = Blockly.Xml.textToDom(xmlTextToRestore);
        Blockly.Xml.domToWorkspace(xml, blocklyApp.workspace);
      }, 0);
    }
  },
  playApplauseSound: function() {
    this.applauseAudioFile.play();
  },
  playOopsSound: function() {
    this.oopsAudioFile.play();
  },
  getLocalStorageCodeKey_: function(levelSetId, levelNumber) {
    return [musicGame.NAME, levelSetId, levelNumber].join('.');
  },
  clearData: function() {
    if (!confirm('Delete all your solutions?')) {
      return;
    }

    var that = this;
    this.getLevelSetsMetadata().forEach(function(levelSetMetadata) {
      levelSetMetadata.levels.forEach(function(level) {
        var key = that.getLocalStorageCodeKey_(
            levelSetMetadata.id, level.number1Indexed - 1);
        delete window.localStorage[key];
      });
    });

    location.reload();
  },
  saveCodeToLocalStorage_: function(levelSetId, levelNumber) {
    // MSIE 11 does not support localStorage on file:// URLs.
    if (!window.localStorage) {
      return;
    }
    var key = this.getLocalStorageCodeKey_(levelSetId, levelNumber);
    var xml = Blockly.Xml.workspaceToDom(blocklyApp.workspace);
    window.localStorage[key] = Blockly.Xml.domToText(xml);
  },
  loadCodeFromLocalStorage_: function(levelSetId, levelNumber) {
    if (!window.localStorage) {
      return;
    }

    var key = this.getLocalStorageCodeKey_(levelSetId, levelNumber);
    return window.localStorage[key];
  },
  getLevelSetId: function() {
    return this.levelSetId_;
  },
  getLevelSetName: function() {
    return LEVEL_SETS[this.levelSetId_].name;
  },
  getLevelSetsMetadata: function() {
    if (!this.levelSetsMetadata) {
      this.levelSetsMetadata = [];

      var that = this;
      for (var levelSetId in LEVEL_SETS) {
        var levels = LEVEL_SETS[levelSetId].levels.map(function(_, index) {
          return {
            number1Indexed: index + 1,
            completed: that.loadCodeFromLocalStorage_(levelSetId, index)
          };
        });

        this.levelSetsMetadata.push({
          id: levelSetId,
          name: LEVEL_SETS[levelSetId].name,
          levels: levels
        });
      }
    }

    return this.levelSetsMetadata;
  },
  getCurrentLevelNumber: function() {
    return this.currentLevelNumber_;
  },
  getNumberOfLevels: function() {
    return this.levelSet_.length;
  },
  getToolboxBlockDefns: function() {
    return this.getCurrentLevelData().toolboxBlockDefns;
  },
  setCurrentLevel: function(current1IndexedLevelNumber) {
    this.currentLevelNumber_ = current1IndexedLevelNumber - 1;
  },
  getCurrentLevelData: function() {
    return this.levelSet_[this.currentLevelNumber_];
  },
  showModal_: function(
      header, messageParagraphs, actionButtonsInfo, onDismissCallback) {
    this.genericModalService.showModal(
        header, messageParagraphs, actionButtonsInfo, onDismissCallback);
  },
  showSimpleModalWithHeader: function(
      header, messageParagraphs, onDismissCallback) {
    this.showModal_(header, messageParagraphs, [], onDismissCallback);
  },
  showSimpleModal: function(messageParagraphs, onDismissCallback) {
    this.showModal_('', messageParagraphs, [], onDismissCallback);
  },
  showInstructions: function() {
    var levelData = this.getCurrentLevelData();

    var messageParagraphs = [levelData.instructions];
    if (levelData.hint) {
      messageParagraphs.push('Hint: ' + levelData.hint);
    }

    var actionButtonsInfo = [];
    if (levelData.expectedLine) {
      actionButtonsInfo.push({
        text: 'Listen to example',
        action: function() {
          var expectedLine = new MusicLine();
          expectedLine.setFromChordsAndDurations(levelData.expectedLine);

          musicPlayer.reset();
          musicPlayer.play(expectedLine, levelData.beatsPerMinute);
        }
      });
    }

    this.showModal_(
        'Instructions', messageParagraphs, actionButtonsInfo,
        function() {
          if (blocklyApp.workspace.topBlocks_.length > 0) {
            document.getElementById('musicGameInstructionsBtn').focus();
          } else {
            document.getElementById(
                blocklyApp.ID_FOR_EMPTY_WORKSPACE_BTN).focus();
          }
        });
  },
  gradeCurrentLevel: function() {
    var currentLevelData = this.getCurrentLevelData();
    var expectedPlayerLine = new MusicLine();
    expectedPlayerLine.setFromChordsAndDurations(
        currentLevelData.expectedLine);

    var correct = musicPlayer.doesPlayerLineEqual(expectedPlayerLine);
    if (correct) {
      this.saveCodeToLocalStorage_(
          this.getLevelSetId(), this.getCurrentLevelNumber());
      this.playApplauseSound();

      if (this.currentLevelNumber_ == this.levelSet_.length - 1) {
        if (this.levelSetId_ == 'tutorial') {
          var messageParagraphs = [
            'Congratulations, you have finished the tutorial levels!',
            'Press Enter to continue to the main game.'];
          this.showSimpleModal(messageParagraphs, function() {
            window.location =
                window.location.protocol + '//' +
                window.location.host + window.location.pathname +
                '?l=1&levelset=game1';
            }
          );
        }
      } else {
        var levelSetId = this.levelSetId_;
        var nextLevelNumber1Indexed = Number(this.currentLevelNumber_ + 2);
        var messageParagraphs = [
          'Good job! You completed the level!',
          'Press Enter to continue to level ' + nextLevelNumber1Indexed + '.'];
        this.showSimpleModal(messageParagraphs, function() {
          window.location =
              window.location.protocol + '//' +
              window.location.host + window.location.pathname +
              '?l=' + nextLevelNumber1Indexed + '&levelset=' + levelSetId;
        });
      }
    } else {
      var playerChords = musicPlayer.getPlayerChords();
      var errorMessage = (
          'Are you playing the right notes? Compare your tune and the ' +
          'desired tune to see if there\'s a difference. Try again!');
      if (currentLevelData.getTargetedFeedback) {
        var targetedMessage = currentLevelData.getTargetedFeedback(
            playerChords);
        if (targetedMessage) {
          errorMessage = targetedMessage;
        }
      }
      this.showSimpleModal([errorMessage], function() {
        document.getElementById('musicGameRunProgramBtn').focus();
      });
    }
  }
});
<|endoftext|>