<commit_before>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var moveCaresAboutBalancesArr = ["petronella", "swing", "pull_by_right", "pull_by_left", "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl", "slide_right_rory_o_moore", "slide_left_rory_o_moore", "custom"];
function moveCaresAboutBalance (move) {
    return 0 <= moveCaresAboutBalancesArr.indexOf(move)
}

// NOTE: this constant is duplicated rubyside in dances_helper.rb. Sync them.
var moveCaresAboutRotationsArr =
    ["do_si_do", "see_saw", "allemande_right", "allemande_left",
     "gypsy_right_shoulder", "gypsy_left_shoulder"];
function moveCaresAboutRotations (move) {
    return 0 <= moveCaresAboutRotationsArr.indexOf(move)
}

// NOTE: this constant is duplicated rubyside in dances_helper.rb. Sync them.
var moveCaresAboutPlacesArr = ["circle_left", "circle_right", "star_left", "star_right"]
function moveCaresAboutPlaces (move) {
    return 0 <= moveCaresAboutPlacesArr.indexOf(move)
}

var degrees2rotations = { 90: "¼",
                         180: "½",
                         270: "¾",
                         360: "once around",
                         450: "1¼",
                         540: "1½",
                         630: "1¾",
                         720: "twice around"}
var degrees2places = { 90: "1 place",
                      180: "2 places",
                      270: "3 places",
                      360: "4 places",
                      450: "5 places",
                      540: "6 places",
                      630: "7 places",
                      720: "8 places"}
function degreesToHumanUnits (degrees,optional_move) {
    if (moveCaresAboutRotations(optional_move) && degrees2rotations[degrees])
        return degrees2rotations[degrees];
    else if (moveCaresAboutPlaces(optional_move) && degrees2places[degrees])
        return degrees2places[degrees];
    return degrees.toString();
}

var anglesForMoveArr = [90,180,270,360,450,540,630,720];
function anglesForMove (move) {
    // move is intentionally ignored
    return anglesForMoveArr
}

function sumBeats(objs,optional_limit) {
    var acc = 0;
    var n = Number.isInteger(optional_limit) ? optional_limit : objs.length;
    for (var i = 0; i < n; i++)
        acc += objs[i].beats;
    return acc;
}

function labelForBeats(beats) {
    if ((beats%16) == 0)
        switch (beats/16) {
        case 0: return "A1";
        case 1: return "A2";
        case 2: return "B1";
        case 3: return "B2";
    }
    return "";
}

function styleForBeats(beats) {
    return beats%32 < 16 ? 'a1b1' : 'a2b2'
}

function moveMenuOptions (formation,who) {
    var r = []; // the return value
    if (who == "") { return []; }
    else switch(formation) {
    case "short_lines":
        r = (who == "everybody") ? ["down_the_hall","up_the_hall"] : []
        break;

    case "short_wavy_lines":
        switch (who) {
        case "everybody":
            r = ["slide_right_rory_o_moore", "slide_left_rory_o_moore",
                 "allemande_and_orbit","hey", "half_a_hey"];
            break;
        case "ladles":
        case "gentlespoons":
        case "partner":
        case "neighbor":
        case "centers":
        case "ones":
        case "twos":
            r = [ "swing",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl"];
            break;
        default: r = []; break;
        }
        break;
    case "long_wavy_lines":

        switch (who) {
        case "everybody":
            r = ["box_circulate",
                 "slide_right_rory_o_moore", "slide_left_rory_o_moore",
                 "hey", "half_a_hey"];
            break;
        case  "partner":
        case  "neighbor":
            r = [ "swing",
                  "allemande_right", "allemande_left","do_si_do", "see_saw",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl"];
            break;
        default: r = []; break;
        }
        break;
    case "long_wavy_line":
        switch (who) {
        case  "ladles":
        case "gentlespoons":
            r = [ "balance","swing",
                  "slide_right_rory_o_moore", "slide_left_rory_o_moore",
                  "allemande_right", "allemande_left","do_si_do", "see_saw",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl",
                ];
            break;
        default: r = []; break;
        }
        break;
    case "square":
    case "custom":
    case "":
    case null:
    case undefined:
        switch (who) {
        case "everybody":
            r = ["circle_left", "circle_right", "star_left", "star_right", "long_lines",
                 "petronella", "balance_the_ring", "to_ocean_wave",
                 "slide_right_rory_o_moore", "slide_left_rory_o_moore", // as in Rory'o'Moore
                 "right_left_through"];
            break;
        case "partner":
        case "neighbor":
            r = [ "swing",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl",
                  "promenade_across",
                  "balance"];
            break;
        case "ladles":
        case "gentlespoons":
            r = [ "chain",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "chain", "pull_by_right", "pull_by_left",
                  // thought about these two, but...it takes too many parameters
                  // better to leave it as "custom":
                  // "allemande_left_with_orbit", "allemande_right_with_orbit",
                  "mad_robin", "hey", "half_a_hey",
                  "balance"];
            break;
        case "ones":
        case "twos":
            r = ["swing","figure_8"];
            break;
        default: r = []; break;
        }
        break;
    }
    r.push("custom");
    return r;
}

// always freshly allocated
function newFigure () {
    return {formation:"square", who:"everybody",beats:8}
}

function defaultFigures (figures) {
    if (figures.length == 0)
        return [newFigure(), newFigure(), newFigure(), newFigure(),
                newFigure(), newFigure(), newFigure(), newFigure()]
    else return figures;
}

(function () {
    var app = angular.module('contra', []);
    var scopeInit = function ($scope) {
        var fctrl42 = this;
        $scope.moveCaresAboutBalance = moveCaresAboutBalance;
        $scope.moveCaresAboutRotations = moveCaresAboutRotations;
        $scope.moveCaresAboutPlaces = moveCaresAboutPlaces;
        $scope.degreesToHumanUnits = degreesToHumanUnits;
        $scope.anglesForMove = anglesForMove;
        $scope.sumBeats = sumBeats;
        $scope.labelForBeats = labelForBeats;
        $scope.styleForBeats = styleForBeats;
        $scope.moveMenuOptions = moveMenuOptions;
        $scope.toJson = angular.toJson;
        $scope.newFigure = newFigure;
        $scope.addFigure = function() {fctrl42.arr.push(newFigure());};
        $scope.deleteFigure = function() {(fctrl42.arr.length>0) && fctrl42.arr.pop()};
        $scope.defaultFigures = defaultFigures;
    }
    app.controller('FiguresController', ['$scope',scopeInit])
})()
<commit_msg>fix dance editor bug on Microsoft IE Internet Explorer https://github.com/dcmorse/contra/issues/78<commit_after>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var moveCaresAboutBalancesArr = ["petronella", "swing", "pull_by_right", "pull_by_left", "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl", "slide_right_rory_o_moore", "slide_left_rory_o_moore", "custom"];
function moveCaresAboutBalance (move) {
    return 0 <= moveCaresAboutBalancesArr.indexOf(move)
}

// NOTE: this constant is duplicated rubyside in dances_helper.rb. Sync them.
var moveCaresAboutRotationsArr =
    ["do_si_do", "see_saw", "allemande_right", "allemande_left",
     "gypsy_right_shoulder", "gypsy_left_shoulder"];
function moveCaresAboutRotations (move) {
    return 0 <= moveCaresAboutRotationsArr.indexOf(move)
}

// NOTE: this constant is duplicated rubyside in dances_helper.rb. Sync them.
var moveCaresAboutPlacesArr = ["circle_left", "circle_right", "star_left", "star_right"]
function moveCaresAboutPlaces (move) {
    return 0 <= moveCaresAboutPlacesArr.indexOf(move)
}

var degrees2rotations = { 90: "¼",
                         180: "½",
                         270: "¾",
                         360: "once around",
                         450: "1¼",
                         540: "1½",
                         630: "1¾",
                         720: "twice around"}
var degrees2places = { 90: "1 place",
                      180: "2 places",
                      270: "3 places",
                      360: "4 places",
                      450: "5 places",
                      540: "6 places",
                      630: "7 places",
                      720: "8 places"}
function degreesToHumanUnits (degrees,optional_move) {
    if (moveCaresAboutRotations(optional_move) && degrees2rotations[degrees])
        return degrees2rotations[degrees];
    else if (moveCaresAboutPlaces(optional_move) && degrees2places[degrees])
        return degrees2places[degrees];
    return degrees.toString();
}

var anglesForMoveArr = [90,180,270,360,450,540,630,720];
function anglesForMove (move) {
    // move is intentionally ignored
    return anglesForMoveArr
}

// Patch to support current IE, source http://stackoverflow.com/questions/31720269/internet-explorer-11-object-doesnt-support-property-or-method-isinteger
isInteger = Number.isInteger || function(value) {
    return typeof value === "number" &&
           isFinite(value) &&
           Math.floor(value) === value;
};

function sumBeats(objs,optional_limit) {
    var acc = 0;
    var n = isInteger(optional_limit) ? optional_limit : objs.length;
    for (var i = 0; i < n; i++)
        acc += objs[i].beats;
    return acc;
}

function labelForBeats(beats) {
    if ((beats%16) == 0)
        switch (beats/16) {
        case 0: return "A1";
        case 1: return "A2";
        case 2: return "B1";
        case 3: return "B2";
    }
    return "";
}

function styleForBeats(beats) {
    return beats%32 < 16 ? 'a1b1' : 'a2b2'
}

function moveMenuOptions (formation,who) {
    var r = []; // the return value
    if (who == "") { return []; }
    else switch(formation) {
    case "short_lines":
        r = (who == "everybody") ? ["down_the_hall","up_the_hall"] : []
        break;

    case "short_wavy_lines":
        switch (who) {
        case "everybody":
            r = ["slide_right_rory_o_moore", "slide_left_rory_o_moore",
                 "allemande_and_orbit","hey", "half_a_hey"];
            break;
        case "ladles":
        case "gentlespoons":
        case "partner":
        case "neighbor":
        case "centers":
        case "ones":
        case "twos":
            r = [ "swing",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl"];
            break;
        default: r = []; break;
        }
        break;
    case "long_wavy_lines":

        switch (who) {
        case "everybody":
            r = ["box_circulate",
                 "slide_right_rory_o_moore", "slide_left_rory_o_moore",
                 "hey", "half_a_hey"];
            break;
        case  "partner":
        case  "neighbor":
            r = [ "swing",
                  "allemande_right", "allemande_left","do_si_do", "see_saw",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl"];
            break;
        default: r = []; break;
        }
        break;
    case "long_wavy_line":
        switch (who) {
        case  "ladles":
        case "gentlespoons":
            r = [ "balance","swing",
                  "slide_right_rory_o_moore", "slide_left_rory_o_moore",
                  "allemande_right", "allemande_left","do_si_do", "see_saw",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl",
                ];
            break;
        default: r = []; break;
        }
        break;
    case "square":
    case "custom":
    case "":
    case null:
    case undefined:
        switch (who) {
        case "everybody":
            r = ["circle_left", "circle_right", "star_left", "star_right", "long_lines",
                 "petronella", "balance_the_ring", "to_ocean_wave",
                 "slide_right_rory_o_moore", "slide_left_rory_o_moore", // as in Rory'o'Moore
                 "right_left_through"];
            break;
        case "partner":
        case "neighbor":
            r = [ "swing",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "pull_by_right", "pull_by_left",
                  "box_the_gnat", "swat_the_flea", "california_twirl", "arizona_twirl",
                  "promenade_across",
                  "balance"];
            break;
        case "ladles":
        case "gentlespoons":
            r = [ "chain",
                  "do_si_do", "see_saw", "allemande_right", "allemande_left",
                  "gypsy_right_shoulder", "gypsy_left_shoulder",
                  "chain", "pull_by_right", "pull_by_left",
                  // thought about these two, but...it takes too many parameters
                  // better to leave it as "custom":
                  // "allemande_left_with_orbit", "allemande_right_with_orbit",
                  "mad_robin", "hey", "half_a_hey",
                  "balance"];
            break;
        case "ones":
        case "twos":
            r = ["swing","figure_8"];
            break;
        default: r = []; break;
        }
        break;
    }
    r.push("custom");
    return r;
}

// always freshly allocated
function newFigure () {
    return {formation:"square", who:"everybody",beats:8}
}

function defaultFigures (figures) {
    if (figures.length == 0)
        return [newFigure(), newFigure(), newFigure(), newFigure(),
                newFigure(), newFigure(), newFigure(), newFigure()]
    else return figures;
}

(function () {
    var app = angular.module('contra', []);
    var scopeInit = function ($scope) {
        var fctrl42 = this;
        $scope.moveCaresAboutBalance = moveCaresAboutBalance;
        $scope.moveCaresAboutRotations = moveCaresAboutRotations;
        $scope.moveCaresAboutPlaces = moveCaresAboutPlaces;
        $scope.degreesToHumanUnits = degreesToHumanUnits;
        $scope.anglesForMove = anglesForMove;
        $scope.sumBeats = sumBeats;
        $scope.labelForBeats = labelForBeats;
        $scope.styleForBeats = styleForBeats;
        $scope.moveMenuOptions = moveMenuOptions;
        $scope.toJson = angular.toJson;
        $scope.newFigure = newFigure;
        $scope.addFigure = function() {fctrl42.arr.push(newFigure());};
        $scope.deleteFigure = function() {(fctrl42.arr.length>0) && fctrl42.arr.pop()};
        $scope.defaultFigures = defaultFigures;
    }
    app.controller('FiguresController', ['$scope',scopeInit])
})()
<|endoftext|>


<commit_before>// dish has been updated / created
$(document).on('ajax:success', 'form.new_dish, form.edit_dish, form.delete_dish', function(e, data, status, xhr){
  $('#dishes-list').html(xhr.responseText);
  $('.dialog').dialog('close');
});

$(document).on('ajax:error', 'form.new_dish, form.edit_dish, form.delete_dish', function(e, xhr, status, error){
  var form = $(this);
  form.parent().html(xhr.responseText);
});

$(document).on('click', '#new-category-button', function(e){
    e.preventDefault();
    var input = $('input[name="category[name]"]');
    var select = $('select[name="dish[category_id]"]');
    input.val(select.find('option:selected').text())
    input.toggle();
    select.toggle();
});

<commit_msg>Correct new-category-button input value<commit_after>// dish has been updated / created
$(document).on('ajax:success', 'form.new_dish, form.edit_dish, form.delete_dish', function(e, data, status, xhr){
  $('#dishes-list').html(xhr.responseText);
  $('.dialog').dialog('close');
});

$(document).on('ajax:error', 'form.new_dish, form.edit_dish, form.delete_dish', function(e, xhr, status, error){
  var form = $(this);
  form.parent().html(xhr.responseText);
});

$(document).on('click', '#new-category-button', function(e){
    e.preventDefault();
    var form = $(this).parents('form');
    var input = form.find('input[name="category[name]"]');
    var select = form.find('select[name="dish[category_id]"]');
    input.val(select.find('option:selected').text());
    input.toggle();
    select.toggle();

    if(!input.is(":visible")){
      input.val('');
    }
});

<|endoftext|>


<commit_before>/*   Copyright (c) 2010-2011, Diaspora Inc.  This file is
 *   licensed under the Affero General Public License version 3 or later.  See
 *   the COPYRIGHT file.
 */
//= require jquery.charcount
//= require mbp-modernizr-custom
//= require mbp-respond.min
//= require mbp-helper
//= require jquery.autoSuggest.custom
$(document).ready(function(){

  $('.shield a').click(function(){
    $(this).parents('.shield_wrapper').remove();
  });
  var showLoader = function(link){
    link.addClass('loading');
  };

  var removeLoader = function(link){
    link.removeClass('loading')
         .toggleClass('active')
         .toggleClass('inactive');
  };

  /* Heart toggle */
  $(".like_action", ".stream").bind("tap click", function(evt){
    evt.preventDefault();
    var link = $(this),
        likeCounter = $(this).closest(".stream_element").find("like_count"),
        href = link.attr("href");

    if(!link.hasClass("loading")){
      if(link.hasClass('inactive')) {
        $.ajax({
          url: href,
          dataType: 'json',
          type: 'POST',
          beforeSend: showLoader(link),
          success: function(data){
            removeLoader(link);
            link.attr("href", href + "/" + data["id"]);

            if(likeCounter){
              likeCounter.text(parseInt(likeCounter.text) + 1);
            }
          }
        });
      }
      else if(link.hasClass("active")){
        $.ajax({
          url: link.attr("href"),
          dataType: 'json',
          type: 'DELETE',
          beforeSend: showLoader(link),
          complete: function(data){
            removeLoader(link);
            link.attr("href", href.replace(/\/\d+$/, ''));

            if(likeCounter){
              likeCounter.text(parseInt(likeCounter.text) - 1);
            }
          }
        });
      }
    }
  });

  /* Reshare */
  $(".reshare_action", ".stream").bind("tap click", function(evt){
    evt.preventDefault();

    var link = $(this),
        href = link.attr("href"),
        confirmText = link.attr('title');

    if(!link.hasClass("loading")) {
      if(link.hasClass('inactive')) {
        if(confirm(confirmText)) {
          $.ajax({
            url: href + "&provider_display_name=mobile",
            dataType: 'json',
            type: 'POST',
            beforeSend: showLoader(link),
            success: function(data){
              removeLoader(link);
            },
            error: function(data){
              removeLoader(link);
              alert("Failed to reshare!");
            }
          });
        }
      }
    }
  });

  /* Show comments */
  $(".show_comments", ".stream").bind("tap click", function(evt){
    evt.preventDefault();
    var link = $(this),
        parent = link.closest(".bottom_bar").first(),
        commentsContainer = function(){ return parent.find(".comment_container").first(); }
        existingCommentsContainer = commentsContainer();

    if( link.hasClass('active') ) {
      existingCommentsContainer.hide();
      if(!link.hasClass('bottom_collapse')){
        link.removeClass('active');
      } else {
        parent.find(".show_comments").first().removeClass('active');
      }

      $('html,body').scrollTop(parent.offset().top - parent.closest(".stream_element").height() - 8);

    } else if( existingCommentsContainer.length > 0) {

      if(!existingCommentsContainer.hasClass('noComments')) {
        $.ajax({
          url: link.attr('href'),
          success: function(data){
            parent.append($(data).find('.comments_container').html());
            link.addClass('active');
            existingCommentsContainer.show();
            scrollToOffset(parent, commentsContainer());
          }
        });
      } else {
        existingCommentsContainer.show();
      }

      link.addClass('active');

    } else {
      $.ajax({
        url: link.attr('href'),
        success: function(data){
          parent.append(data);
          link.addClass('active');
          scrollToOffset(parent, commentsContainer());
        }
      });
    }

  });

  var scrollToOffset = function(parent, commentsContainer){
    var commentCount = commentsContainer.find("li.comment").length;
    if( commentCount > 3 ) {
      var lastComment = commentsContainer.find("li:nth-child("+(commentCount-4)+")");
      $('html,body').animate({
        scrollTop: lastComment.offset().top
      }, 1000);
    }
  };

  $(".stream").delegate("a.comment_action", "tap click", function(evt){
    evt.preventDefault();
    var link = $(this);

    if(link.hasClass('inactive')) {
      var parent = link.closest(".bottom_bar").first(),
          container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container').first();

      $.ajax({
        url: link.attr('href'),
        beforeSend: function(){
          link.addClass('loading');
        },
        context: link,
        success: function(data){
          var textarea = function(target) { return target.closest(".stream_element").find('textarea.comment_box').first()[0] };
          link.removeClass('loading')

          if(!link.hasClass("add_comment_bottom_link")){
            link.removeClass('inactive');
          }

          container.hide();
          parent.append(data);

          MBP.autogrow(textarea($(this)));
        }
      });
    }
  });

  $(".stream").delegate("a.cancel_new_comment", "tap click", function(evt){
    evt.preventDefault();
    var link = $(this);
        form = link.closest("form"),
        commentActionLink = link.closest(".bottom_bar").find("a.comment_action").first();
        container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container');

    if(container.length > 0 ){
      container.first().show();
    }

    commentActionLink.addClass("inactive");
    form.remove();
  });

  $(".new_comment").live("submit", function(evt){
    evt.preventDefault();
    var form = $(this);

    $.post(form.attr('action')+"?format=mobile", form.serialize(), function(data){
      var bottomBar = form.closest('.bottom_bar').first(),
          container = bottomBar.find('.add_comment_bottom_link_container'),
          commentActionLink = bottomBar.find("a.comment_action").first();
          reactionLink = bottomBar.find(".show_comments").first(),
          commentCount = bottomBar.find(".comment_count");

      if(container.length > 0) {
        container.before(data);
        form.remove();
        container.show();

      } else {
        var container = $("

"),
            comments = $("

");

        comments.html(data);
        container.append(comments);
        form.remove();
        container.appendTo(bottomBar)
      }

      reactionLink.text(reactionLink.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
      commentCount.text(commentCount.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
      commentActionLink.addClass("inactive");
    }, 'html');
  });

  $(".service_icon").bind("tap click", function(evt) {
    var service = $(this).toggleClass("dim"),
      selectedServices = $("#new_status_message .service_icon:not(.dim)"),
      provider = service.attr("id"),
      hiddenField = $("#new_status_message input[name='services[]'][value='" + provider + "']"),
      publisherMaxChars = 40000,
      serviceMaxChars;

    $("#new_status_message .counter").remove();

    $.each(selectedServices, function() {
      serviceMaxChars = parseInt($(this).attr("maxchar"));
      if(publisherMaxChars > serviceMaxChars) {
        publisherMaxChars = serviceMaxChars;
      }
    });

    $('#status_message_text').charCount({allowed: publisherMaxChars, warning: publisherMaxChars/10 });

    if(hiddenField.length > 0) { hiddenField.remove(); }
    else {
      $("#new_status_message").append(
        $("<input/>", {
          name: "services[]",
          type: "hidden",
          value: provider
        })
      );
    }
  });

  $("#submit_new_message").bind("tap click", function(evt){
    evt.preventDefault();
    $("#new_status_message").submit();
  });

});
<commit_msg>With mobile click "show" in #nsfw-posts links to top of page Fix: #3564<commit_after>/*   Copyright (c) 2010-2011, Diaspora Inc.  This file is
 *   licensed under the Affero General Public License version 3 or later.  See
 *   the COPYRIGHT file.
 */
//= require jquery.charcount
//= require mbp-modernizr-custom
//= require mbp-respond.min
//= require mbp-helper
//= require jquery.autoSuggest.custom
$(document).ready(function(){

  $('.shield a').click(function(){
    $(this).parents('.shield_wrapper').remove();
    return false;
  });
  var showLoader = function(link){
    link.addClass('loading');
  };

  var removeLoader = function(link){
    link.removeClass('loading')
         .toggleClass('active')
         .toggleClass('inactive');
  };

  /* Heart toggle */
  $(".like_action", ".stream").bind("tap click", function(evt){
    evt.preventDefault();
    var link = $(this),
        likeCounter = $(this).closest(".stream_element").find("like_count"),
        href = link.attr("href");

    if(!link.hasClass("loading")){
      if(link.hasClass('inactive')) {
        $.ajax({
          url: href,
          dataType: 'json',
          type: 'POST',
          beforeSend: showLoader(link),
          success: function(data){
            removeLoader(link);
            link.attr("href", href + "/" + data["id"]);

            if(likeCounter){
              likeCounter.text(parseInt(likeCounter.text) + 1);
            }
          }
        });
      }
      else if(link.hasClass("active")){
        $.ajax({
          url: link.attr("href"),
          dataType: 'json',
          type: 'DELETE',
          beforeSend: showLoader(link),
          complete: function(data){
            removeLoader(link);
            link.attr("href", href.replace(/\/\d+$/, ''));

            if(likeCounter){
              likeCounter.text(parseInt(likeCounter.text) - 1);
            }
          }
        });
      }
    }
  });

  /* Reshare */
  $(".reshare_action", ".stream").bind("tap click", function(evt){
    evt.preventDefault();

    var link = $(this),
        href = link.attr("href"),
        confirmText = link.attr('title');

    if(!link.hasClass("loading")) {
      if(link.hasClass('inactive')) {
        if(confirm(confirmText)) {
          $.ajax({
            url: href + "&provider_display_name=mobile",
            dataType: 'json',
            type: 'POST',
            beforeSend: showLoader(link),
            success: function(data){
              removeLoader(link);
            },
            error: function(data){
              removeLoader(link);
              alert("Failed to reshare!");
            }
          });
        }
      }
    }
  });

  /* Show comments */
  $(".show_comments", ".stream").bind("tap click", function(evt){
    evt.preventDefault();
    var link = $(this),
        parent = link.closest(".bottom_bar").first(),
        commentsContainer = function(){ return parent.find(".comment_container").first(); }
        existingCommentsContainer = commentsContainer();

    if( link.hasClass('active') ) {
      existingCommentsContainer.hide();
      if(!link.hasClass('bottom_collapse')){
        link.removeClass('active');
      } else {
        parent.find(".show_comments").first().removeClass('active');
      }

      $('html,body').scrollTop(parent.offset().top - parent.closest(".stream_element").height() - 8);

    } else if( existingCommentsContainer.length > 0) {

      if(!existingCommentsContainer.hasClass('noComments')) {
        $.ajax({
          url: link.attr('href'),
          success: function(data){
            parent.append($(data).find('.comments_container').html());
            link.addClass('active');
            existingCommentsContainer.show();
            scrollToOffset(parent, commentsContainer());
          }
        });
      } else {
        existingCommentsContainer.show();
      }

      link.addClass('active');

    } else {
      $.ajax({
        url: link.attr('href'),
        success: function(data){
          parent.append(data);
          link.addClass('active');
          scrollToOffset(parent, commentsContainer());
        }
      });
    }

  });

  var scrollToOffset = function(parent, commentsContainer){
    var commentCount = commentsContainer.find("li.comment").length;
    if( commentCount > 3 ) {
      var lastComment = commentsContainer.find("li:nth-child("+(commentCount-4)+")");
      $('html,body').animate({
        scrollTop: lastComment.offset().top
      }, 1000);
    }
  };

  $(".stream").delegate("a.comment_action", "tap click", function(evt){
    evt.preventDefault();
    var link = $(this);

    if(link.hasClass('inactive')) {
      var parent = link.closest(".bottom_bar").first(),
          container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container').first();

      $.ajax({
        url: link.attr('href'),
        beforeSend: function(){
          link.addClass('loading');
        },
        context: link,
        success: function(data){
          var textarea = function(target) { return target.closest(".stream_element").find('textarea.comment_box').first()[0] };
          link.removeClass('loading')

          if(!link.hasClass("add_comment_bottom_link")){
            link.removeClass('inactive');
          }

          container.hide();
          parent.append(data);

          MBP.autogrow(textarea($(this)));
        }
      });
    }
  });

  $(".stream").delegate("a.cancel_new_comment", "tap click", function(evt){
    evt.preventDefault();
    var link = $(this);
        form = link.closest("form"),
        commentActionLink = link.closest(".bottom_bar").find("a.comment_action").first();
        container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container');

    if(container.length > 0 ){
      container.first().show();
    }

    commentActionLink.addClass("inactive");
    form.remove();
  });

  $(".new_comment").live("submit", function(evt){
    evt.preventDefault();
    var form = $(this);

    $.post(form.attr('action')+"?format=mobile", form.serialize(), function(data){
      var bottomBar = form.closest('.bottom_bar').first(),
          container = bottomBar.find('.add_comment_bottom_link_container'),
          commentActionLink = bottomBar.find("a.comment_action").first();
          reactionLink = bottomBar.find(".show_comments").first(),
          commentCount = bottomBar.find(".comment_count");

      if(container.length > 0) {
        container.before(data);
        form.remove();
        container.show();

      } else {
        var container = $("

"),
            comments = $("

");

        comments.html(data);
        container.append(comments);
        form.remove();
        container.appendTo(bottomBar)
      }

      reactionLink.text(reactionLink.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
      commentCount.text(commentCount.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
      commentActionLink.addClass("inactive");
    }, 'html');
  });

  $(".service_icon").bind("tap click", function(evt) {
    var service = $(this).toggleClass("dim"),
      selectedServices = $("#new_status_message .service_icon:not(.dim)"),
      provider = service.attr("id"),
      hiddenField = $("#new_status_message input[name='services[]'][value='" + provider + "']"),
      publisherMaxChars = 40000,
      serviceMaxChars;

    $("#new_status_message .counter").remove();

    $.each(selectedServices, function() {
      serviceMaxChars = parseInt($(this).attr("maxchar"));
      if(publisherMaxChars > serviceMaxChars) {
        publisherMaxChars = serviceMaxChars;
      }
    });

    $('#status_message_text').charCount({allowed: publisherMaxChars, warning: publisherMaxChars/10 });

    if(hiddenField.length > 0) { hiddenField.remove(); }
    else {
      $("#new_status_message").append(
        $("<input/>", {
          name: "services[]",
          type: "hidden",
          value: provider
        })
      );
    }
  });

  $("#submit_new_message").bind("tap click", function(evt){
    evt.preventDefault();
    $("#new_status_message").submit();
  });

});
<|endoftext|>


<commit_before>//= require vendor/jquery-1.7.js
//= require vendor/jquery.timeago.js
//= require vendor/jquery.cookie.js
//= require vendor/jquery.tipsy.js
//= require vendor/sproutcore.js
//= require vendor/sproutcore-datastore.js
//= require_tree ./vendor/sproutcore-2.0
//= require vendor/pusher-1.6.min.js
//= require vendor/highcharts.js

<commit_msg>[dist] Require vendored `ansiparse`<commit_after>//= require vendor/jquery-1.7.js
//= require vendor/jquery.timeago.js
//= require vendor/jquery.cookie.js
//= require vendor/jquery.tipsy.js
//= require vendor/sproutcore.js
//= require vendor/sproutcore-datastore.js
//= require_tree ./vendor/sproutcore-2.0
//= require vendor/pusher-1.6.min.js
//= require vendor/highcharts.js
//= require vendor/ansiparse.js

<|endoftext|>


<commit_before>/**
 * Created by dungvn3000 on 2/11/14.
 */

Ext.define('sunerp.store.BaseStore', {
    extend: 'Ext.data.Store',
    requires: [
        'sunerp.store.Restx'
    ],
    autoLoad: false,
    remoteSort: true,
    remoteFilter: true,
    pageSize: 50,
    constructor: function () {
        this.callParent(arguments);
        this.on('metachange', function (store, meta) {
            var model = store.getNewRecords()[0];
            model.set('id', meta.id);
            model.setDirty(false);
        });
    }
});<commit_msg>Set default pageSize 50 to 100.<commit_after>/**
 * Created by dungvn3000 on 2/11/14.
 */

Ext.define('sunerp.store.BaseStore', {
    extend: 'Ext.data.Store',
    requires: [
        'sunerp.store.Restx'
    ],
    autoLoad: false,
    remoteSort: true,
    remoteFilter: true,
    pageSize: 100,
    constructor: function () {
        this.callParent(arguments);
        this.on('metachange', function (store, meta) {
            var model = store.getNewRecords()[0];
            model.set('id', meta.id);
            model.setDirty(false);
        });
    }
});<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import Option from '../_common/Option';

export default class MarketSubmarketPicker extends Component {

	static propTypes = {
		marketTree: PropTypes.object.isRequired,
		onChange: PropTypes.func.isRequired,
		allOptionShown: PropTypes.bool.isRequired,
		showMarkets: PropTypes.array,
		value: PropTypes.string,
	};

	render() {
		const { marketTree, onChange, allOptionShown, showMarkets, value } = this.props;
		const markets = Object.keys(marketTree)
			.filter(market => !(showMarkets && showMarkets.includes(marketTree[market].display_name)));

		const submarkets = markets.map(market => {
			const marketSubTree = marketTree[market];
			const marketOpt = { value: market, name: marketSubTree.display_name };

			// unicode is used as &nbsp is escaped by react and css styling on select is not reliable
			const submarketOptArr = Object.keys(marketSubTree.submarkets)
				.map(submarket => (
					{
						value: submarket,
						name: '\u00a0\u00a0\u00a0\u00a0' + marketSubTree.submarkets[submarket].display_name,
					}
				));
			submarketOptArr.unshift(marketOpt);
			return submarketOptArr;
		});

		const flattenSubmarkets = [].concat.apply([], submarkets);

		return (
			<select
				defaultValue={value}
				className="market-submarket-picker"
				onChange={e => onChange(e.target.value)}
			>
				{allOptionShown && <Option text="All" value="all" />}
				{allOptionShown && <Option text="Favorites" value="all" />}
				{flattenSubmarkets.map((opt, k) =>
					<option key={k} value={opt.value}>{opt.name}</option>
				)}
			</select>
		);
	}
}
<commit_msg>Fix favorites filtering<commit_after>import React, { PropTypes, Component } from 'react';
import Option from '../_common/Option';

export default class MarketSubmarketPicker extends Component {

	static propTypes = {
		marketTree: PropTypes.object.isRequired,
		onChange: PropTypes.func.isRequired,
		allOptionShown: PropTypes.bool.isRequired,
		showMarkets: PropTypes.array,
		value: PropTypes.string,
	};

	render() {
		const { marketTree, onChange, allOptionShown, showMarkets, value } = this.props;
		const markets = Object.keys(marketTree)
			.filter(market => !(showMarkets && showMarkets.includes(marketTree[market].display_name)));

		const submarkets = markets.map(market => {
			const marketSubTree = marketTree[market];
			const marketOpt = { value: market, name: marketSubTree.display_name };

			// unicode is used as &nbsp is escaped by react and css styling on select is not reliable
			const submarketOptArr = Object.keys(marketSubTree.submarkets)
				.map(submarket => (
					{
						value: submarket,
						name: '\u00a0\u00a0\u00a0\u00a0' + marketSubTree.submarkets[submarket].display_name,
					}
				));
			submarketOptArr.unshift(marketOpt);
			return submarketOptArr;
		});

		const flattenSubmarkets = [].concat.apply([], submarkets);

		return (
			<select
				defaultValue={value}
				className="market-submarket-picker"
				onChange={e => onChange(e.target.value)}
			>
				{allOptionShown && <Option text="All" value="all" />}
				{allOptionShown && <Option text="Favorites" value="favorites" />}
				{flattenSubmarkets.map((opt, k) =>
					<option key={k} value={opt.value}>{opt.name}</option>
				)}
			</select>
		);
	}
}
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'

import { makeSelectTranslation, makeSelectLoading, makeSelectError } from 'containers/App/selectors'
import H1 from 'components/H1'
import H2 from 'components/H2'
import TranslatedText from 'components/TranslatedText'
import CenteredSection from './CenteredSection'
import Form from './Form'
import Input from './Input'
import Section from './Section'
import { loadTranslation } from '../App/actions'
import { increment, decrement, changeText } from './actions'
import { makeSelectCount, makeSelectText } from './selectors'

export class HomePage extends PureComponent {
  static propTypes = {
    loading: PropTypes.bool,
    error: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.bool
    ]),
    translation: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.bool
    ]),
    onSubmitForm: PropTypes.func,
    text: PropTypes.string,
    onChangeText: PropTypes.func
  }

  // When initial state text is not null, submit the form to load translation
  componentDidMount = () => {
    if (this.props.text && this.props.text.trim().length > 0) {
      this.props.onSubmitForm()
    }
  }

  render () {
    const { loading, error, translation } = this.props
    const translationListProps = {
      loading,
      error,
      translation
    }

    return (

        <Helmet
          title='Home Page'
          meta={[
            { name: 'description', content: 'A React.js Boilerplate application homepage' }
          ]}
        />

          <CenteredSection>

Start your project right away!

A highly scalable, offline-first foundation based on the popular react-boilerplate.

          </CenteredSection>

Try me! Redux Saga API Call

            <Form onSubmit={this.props.onSubmitForm}>

                Enter some text to see it translated into Yoda-Speak:

                <Input
                  id='text'
                  type='text'
                  placeholder='You can use this boilerplate to get started right away!'
                  value={this.props.text}
                  onChange={this.props.onChangeText}
                />

            </Form>
            <TranslatedText {...translationListProps} />

Simple counter using Redux actions

          Current counter value: {this.props.count}

            <button className='primary' onClick={this.props.onIncrement}>Increment</button>
            <button className='secondary' onClick={this.props.onDecrement}>Decrement</button>

    )
  }
}

const mapStateToProps = createStructuredSelector({
  translation: makeSelectTranslation(),
  text: makeSelectText(),
  loading: makeSelectLoading(),
  error: makeSelectError(),
  count: makeSelectCount()
})

export function mapDispatchToProps (dispatch) {
  return {
    onChangeText: (evt) => dispatch(changeText(evt.target.value)),
    onSubmitForm: (evt) => {
      if (evt !== undefined && evt.preventDefault) evt.preventDefault()
      dispatch(loadTranslation())
    },
    onIncrement: () => dispatch(increment()),
    onDecrement: () => dispatch(decrement())
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomePage)
<commit_msg>refactor connect and compose for component<commit_after>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { createStructuredSelector } from 'reselect'

import injectReducer from 'utils/injectReducer'
import injectSaga from 'utils/injectSaga'
import reducer from './reducer'
import saga from './saga'

import { makeSelectTranslation, makeSelectLoading, makeSelectError } from 'containers/App/selectors'
import H1 from 'components/H1'
import H2 from 'components/H2'
import TranslatedText from 'components/TranslatedText'
import CenteredSection from './CenteredSection'
import Form from './Form'
import Input from './Input'
import Section from './Section'
import { loadTranslation } from '../App/actions'
import { increment, decrement, changeText } from './actions'
import { makeSelectCount, makeSelectText } from './selectors'

export class HomePage extends PureComponent {
  static propTypes = {
    loading: PropTypes.bool,
    error: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.bool
    ]),
    translation: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.bool
    ]),
    onSubmitForm: PropTypes.func,
    text: PropTypes.string,
    onChangeText: PropTypes.func
  }

  // When initial state text is not null, submit the form to load translation
  componentDidMount = () => {
    if (this.props.text && this.props.text.trim().length > 0) {
      this.props.onSubmitForm()
    }
  }

  render () {
    const { loading, error, translation } = this.props
    const translationListProps = {
      loading,
      error,
      translation
    }

    return (

        <Helmet
          title='Home Page'
          meta={[
            { name: 'description', content: 'A React.js Boilerplate application homepage' }
          ]}
        />

          <CenteredSection>

Start your project right away!

A highly scalable, offline-first foundation based on the popular react-boilerplate.

          </CenteredSection>

Try me! Redux Saga API Call

            <Form onSubmit={this.props.onSubmitForm}>

                Enter some text to see it translated into Yoda-Speak:

                <Input
                  id='text'
                  type='text'
                  placeholder='You can use this boilerplate to get started right away!'
                  value={this.props.text}
                  onChange={this.props.onChangeText}
                />

            </Form>
            <TranslatedText {...translationListProps} />

Simple counter using Redux actions

          Current counter value: {this.props.count}

            <button className='primary' onClick={this.props.onIncrement}>Increment</button>
            <button className='secondary' onClick={this.props.onDecrement}>Decrement</button>

    )
  }
}

const mapStateToProps = createStructuredSelector({
  translation: makeSelectTranslation(),
  text: makeSelectText(),
  loading: makeSelectLoading(),
  error: makeSelectError(),
  count: makeSelectCount()
})

export function mapDispatchToProps (dispatch) {
  return {
    onChangeText: (evt) => dispatch(changeText(evt.target.value)),
    onSubmitForm: (evt) => {
      if (evt !== undefined && evt.preventDefault) evt.preventDefault()
      dispatch(loadTranslation())
    },
    onIncrement: () => dispatch(increment()),
    onDecrement: () => dispatch(decrement())
  }
}

const withConnect = connect(mapStateToProps, mapDispatchToProps)
const withReducer = injectReducer({ key: 'home', reducer })
const withSaga = injectSaga({ key: 'home', saga })

export default compose(
  withReducer,
  withSaga,
  withConnect
)(HomePage)
<|endoftext|>


<commit_before>/**
 * WARNING: changes to this file in particular can cause significant changes to
 * the results of render performance benchmarks.
 */
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
import I18nManager from '../I18nManager';
import mapKeyValue from '../../modules/mapKeyValue';
import prefixInlineStyles from './prefixInlineStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';

const vendorPrefixPattern = /^(webkit|moz|ms)/;

const createCacheKey = id => {
  const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
  return `${prefix}-${id}`;
};

const classListToString = list => list.join(' ').trim();

class StyleRegistry {
  constructor() {
    this.cache = {};
    this.styleManager = new StyleManager();
  }

  getStyleSheetHtml() {
    return this.styleManager.getStyleSheetHtml();
  }

  /**
   * Registers and precaches a React Native style object to HTML class names
   */
  register(flatStyle) {
    const id = ReactNativePropRegistry.register(flatStyle);
    const key = createCacheKey(id);
    const style = createReactDOMStyle(flatStyle);
    const classList = mapKeyValue(style, (prop, value) => {
      if (value != null) {
        return this.styleManager.setDeclaration(prop, value);
      }
    });
    const className = classList.join(' ').trim();
    this.cache[key] = { classList, className };
    return id;
  }

  /**
   * Resolves a React Native style object to DOM attributes
   */
  resolve(reactNativeStyle) {
    if (!reactNativeStyle) {
      return undefined;
    }

    // fast and cachable
    if (typeof reactNativeStyle === 'number') {
      const key = createCacheKey(reactNativeStyle);
      return this._resolveStyleIfNeeded(key, reactNativeStyle);
    }

    // resolve a plain RN style object
    if (!Array.isArray(reactNativeStyle)) {
      return this._resolveStyle(reactNativeStyle);
    }

    // flatten the style array
    // cache resolved props when all styles are registered
    // otherwise fallback to resolving
    const flatArray = flattenArray(reactNativeStyle);
    let isArrayOfNumbers = true;
    for (let i = 0; i < flatArray.length; i++) {
      if (typeof flatArray[i] !== 'number') {
        isArrayOfNumbers = false;
        break;
      }
    }
    const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
    return this._resolveStyleIfNeeded(key, flatArray);
  }

  /**
   * Resolves a React Native style object to DOM attributes, accounting for
   * the existing styles applied to the DOM node.
   *
   * To determine the next style, some of the existing DOM state must be
   * converted back into React Native styles.
   */
  resolveStateful(rnStyleNext, domStyleProps) {
    // Convert the DOM classList back into a React Native form
    // Preserves unrecognized class names.
    const rnStyleProps = domStyleProps.classList.reduce(
      (styleProps, className) => {
        const { prop, value } = this.styleManager.getDeclaration(className);
        if (prop) {
          styleProps.style[prop] = value;
        } else {
          styleProps.classList.push(className);
        }
        return styleProps;
      },
      { classList: [], style: {} }
    );

    // DOM style may include vendor prefixes and properties set by other libraries.
    // Preserve it but transform back into React DOM style.
    const rdomStyle = Object.keys(domStyleProps.style).reduce((acc, styleName) => {
      const value = domStyleProps.style[styleName];
      if (value !== '') {
        const reactStyleName = vendorPrefixPattern.test(styleName)
          ? styleName.charAt(0).toUpperCase() + styleName.slice(1)
          : styleName;
        acc[reactStyleName] = value;
      }
      return acc;
    }, {});

    // Create next DOM style props from current and next RN styles
    const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
      rnStyleProps.style,
      rnStyleNext
    ]);
    // Add the current class names not managed by React Native
    rdomClassListNext.push(...rnStyleProps.classList);
    // Next class names take priority over current inline styles
    const style = { ...rdomStyle };
    rdomClassListNext.forEach(className => {
      const { prop } = this.styleManager.getDeclaration(className);
      if (style[prop]) {
        style[prop] = '';
      }
    });
    // Next inline styles take priority over current inline styles
    Object.assign(style, rdomStyleNext);
    const className = classListToString(rdomClassListNext);

    return { className, style };
  }

  /**
   * Resolves a React Native style object
   */
  _resolveStyle(reactNativeStyle) {
    const domStyle = createReactDOMStyle(flattenStyle(reactNativeStyle));

    const props = Object.keys(domStyle).reduce(
      (props, styleProp) => {
        const value = domStyle[styleProp];
        if (value != null) {
          const className = this.styleManager.getClassName(styleProp, value);
          if (className) {
            props.classList.push(className);
          } else {
            if (!props.style) {
              props.style = {};
            }
            // 4x slower render
            props.style[styleProp] = value;
          }
        }
        return props;
      },
      { classList: [] }
    );

    props.className = classListToString(props.classList);
    if (props.style) {
      props.style = prefixInlineStyles(props.style);
    }
    return props;
  }

  /**
  * Caching layer over 'resolveStyle'
   */
  _resolveStyleIfNeeded(key, style) {
    if (key) {
      if (!this.cache[key]) {
        // slow: convert style object to props and cache
        this.cache[key] = this._resolveStyle(style);
      }
      return this.cache[key];
    }
    return this._resolveStyle(style);
  }
}

module.exports = StyleRegistry;
<commit_msg>Rename some variables in StyleRegistry<commit_after>/**
 * WARNING: changes to this file in particular can cause significant changes to
 * the results of render performance benchmarks.
 */
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
import I18nManager from '../I18nManager';
import mapKeyValue from '../../modules/mapKeyValue';
import prefixInlineStyles from './prefixInlineStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';

const vendorPrefixPattern = /^(webkit|moz|ms)/;

const createCacheKey = id => {
  const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
  return `${prefix}-${id}`;
};

const classListToString = list => list.join(' ').trim();

class StyleRegistry {
  constructor() {
    this.cache = {};
    this.styleManager = new StyleManager();
  }

  getStyleSheetHtml() {
    return this.styleManager.getStyleSheetHtml();
  }

  /**
   * Registers and precaches a React Native style object to HTML class names
   */
  register(flatStyle) {
    const id = ReactNativePropRegistry.register(flatStyle);
    const key = createCacheKey(id);
    const style = createReactDOMStyle(flatStyle);
    const classList = mapKeyValue(style, (prop, value) => {
      if (value != null) {
        return this.styleManager.setDeclaration(prop, value);
      }
    });
    const className = classList.join(' ').trim();
    this.cache[key] = { classList, className };
    return id;
  }

  /**
   * Resolves a React Native style object to DOM attributes
   */
  resolve(reactNativeStyle) {
    if (!reactNativeStyle) {
      return undefined;
    }

    // fast and cachable
    if (typeof reactNativeStyle === 'number') {
      const key = createCacheKey(reactNativeStyle);
      return this._resolveStyleIfNeeded(key, reactNativeStyle);
    }

    // resolve a plain RN style object
    if (!Array.isArray(reactNativeStyle)) {
      return this._resolveStyle(reactNativeStyle);
    }

    // flatten the style array
    // cache resolved props when all styles are registered
    // otherwise fallback to resolving
    const flatArray = flattenArray(reactNativeStyle);
    let isArrayOfNumbers = true;
    for (let i = 0; i < flatArray.length; i++) {
      if (typeof flatArray[i] !== 'number') {
        isArrayOfNumbers = false;
        break;
      }
    }
    const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
    return this._resolveStyleIfNeeded(key, flatArray);
  }

  /**
   * Resolves a React Native style object to DOM attributes, accounting for
   * the existing styles applied to the DOM node.
   *
   * To determine the next style, some of the existing DOM state must be
   * converted back into React Native styles.
   */
  resolveStateful(rnStyleNext, { classList: domClassList, style: domStyle }) {
    // Convert the DOM classList back into a React Native form
    // Preserves unrecognized class names.
    const { classList: rnClassList, style: rnStyle } = domClassList.reduce(
      (styleProps, className) => {
        const { prop, value } = this.styleManager.getDeclaration(className);
        if (prop) {
          styleProps.style[prop] = value;
        } else {
          styleProps.classList.push(className);
        }
        return styleProps;
      },
      { classList: [], style: {} }
    );

    // DOM style may include vendor prefixes and properties set by other libraries.
    // Preserve it but transform back into React DOM style.
    const rdomStyle = Object.keys(domStyle).reduce((acc, styleName) => {
      const value = domStyle[styleName];
      if (value !== '') {
        const reactStyleName = vendorPrefixPattern.test(styleName)
          ? styleName.charAt(0).toUpperCase() + styleName.slice(1)
          : styleName;
        acc[reactStyleName] = value;
      }
      return acc;
    }, {});

    // Create next DOM style props from current and next RN styles
    const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
      rnStyle,
      rnStyleNext
    ]);
    // Next class names take priority over current inline styles
    const style = { ...rdomStyle };
    rdomClassListNext.forEach(className => {
      const { prop } = this.styleManager.getDeclaration(className);
      if (style[prop]) {
        style[prop] = '';
      }
    });
    // Next inline styles take priority over current inline styles
    Object.assign(style, rdomStyleNext);
    // Add the current class names not managed by React Native
    const className = classListToString(rdomClassListNext.concat(rnClassList));

    return { className, style };
  }

  /**
   * Resolves a React Native style object
   */
  _resolveStyle(reactNativeStyle) {
    const domStyle = createReactDOMStyle(flattenStyle(reactNativeStyle));

    const props = Object.keys(domStyle).reduce(
      (props, styleProp) => {
        const value = domStyle[styleProp];
        if (value != null) {
          const className = this.styleManager.getClassName(styleProp, value);
          if (className) {
            props.classList.push(className);
          } else {
            if (!props.style) {
              props.style = {};
            }
            // 4x slower render
            props.style[styleProp] = value;
          }
        }
        return props;
      },
      { classList: [] }
    );

    props.className = classListToString(props.classList);
    if (props.style) {
      props.style = prefixInlineStyles(props.style);
    }
    return props;
  }

  /**
  * Caching layer over 'resolveStyle'
   */
  _resolveStyleIfNeeded(key, style) {
    if (key) {
      if (!this.cache[key]) {
        // slow: convert style object to props and cache
        this.cache[key] = this._resolveStyle(style);
      }
      return this.cache[key];
    }
    return this._resolveStyle(style);
  }
}

module.exports = StyleRegistry;
<|endoftext|>


<commit_before>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import Controller from '@ember/controller';
import RSVP from 'rsvp';
import config from 'ghost-admin/config/environment';
import {
    UnsupportedMediaTypeError,
    isRequestEntityTooLargeError,
    isUnsupportedMediaTypeError
} from 'ghost-admin/services/ajax';
import {computed} from '@ember/object';
import {isBlank} from '@ember/utils';
import {isArray as isEmberArray} from '@ember/array';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';

const {Promise} = RSVP;

const IMPORT_MIME_TYPES = [
    'application/json',
    'application/zip',
    'application/x-zip-compressed'
];

const JSON_EXTENSION = ['json'];
const JSON_MIME_TYPE = ['application/json'];

const YAML_EXTENSION = ['yaml'];
const YAML_MIME_TYPE = [
    'text/vnd.yaml',
    'application/vnd.yaml',
    'text/x-yaml',
    'application/x-yaml'
];

export default Controller.extend({
    ajax: service(),
    config: service(),
    feature: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),

    importErrors: null,
    importSuccessful: false,
    showDeleteAllModal: false,
    submitting: false,
    uploadButtonText: 'Import',

    importMimeType: null,
    jsonExtension: null,
    jsonMimeType: null,
    yamlExtension: null,
    yamlMimeType: null,
    init() {
        this._super(...arguments);
        this.importMimeType = IMPORT_MIME_TYPES;
        this.jsonExtension = JSON_EXTENSION;
        this.jsonMimeType = JSON_MIME_TYPE;
        this.yamlExtension = YAML_EXTENSION;
        this.yamlMimeType = YAML_MIME_TYPE;
    },

    subscriptionSettings: computed('settings.membersSubscriptionSettings', function () {
        let subscriptionSettings = this.parseSubscriptionSettings(this.get('settings.membersSubscriptionSettings'));
        let stripeProcessor = subscriptionSettings.paymentProcessors.find((proc) => {
            return (proc.adapter === 'stripe');
        });
        let monthlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'month');
        let yearlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'year');
        monthlyPlan.dollarAmount = monthlyPlan.amount ? (monthlyPlan.amount / 100) : '';
        yearlyPlan.dollarAmount = yearlyPlan.amount ? (yearlyPlan.amount / 100) : '';
        stripeProcessor.config.plans = {
            monthly: monthlyPlan,
            yearly: yearlyPlan
        };
        subscriptionSettings.stripeConfig = stripeProcessor.config;
        return subscriptionSettings;
    }),

    actions: {
        onUpload(file) {
            let formData = new FormData();
            let notifications = this.notifications;
            let currentUserId = this.get('session.user.id');
            let dbUrl = this.get('ghostPaths.url').api('db');

            this.set('uploadButtonText', 'Importing');
            this.set('importErrors', null);
            this.set('importSuccessful', false);

            return this._validate(file).then(() => {
                formData.append('importfile', file);

                return this.ajax.post(dbUrl, {
                    data: formData,
                    dataType: 'json',
                    cache: false,
                    contentType: false,
                    processData: false
                });
            }).then((response) => {
                let store = this.store;

                this.set('importSuccessful', true);

                if (response.problems) {
                    this.set('importErrors', response.problems);
                }

                // Clear the store, so that all the new data gets fetched correctly.
                store.unloadAll();

                // NOTE: workaround for behaviour change in Ember 2.13
                // store.unloadAll has some async tendencies so we need to schedule
                // the reload of the current user once the unload has finished
                // https://github.com/emberjs/data/issues/4963
                run.schedule('destroy', this, () => {
                    // Reload currentUser and set session
                    this.set('session.user', store.findRecord('user', currentUserId));

                    // TODO: keep as notification, add link to view content
                    notifications.showNotification('Import successful.', {key: 'import.upload.success'});

                    // reload settings
                    return this.settings.reload().then((settings) => {
                        this.feature.fetch();
                        this.config.set('blogTitle', settings.get('title'));
                    });
                });
            }).catch((response) => {
                if (isUnsupportedMediaTypeError(response) || isRequestEntityTooLargeError(response)) {
                    this.set('importErrors', [response]);
                } else if (response && response.payload.errors && isEmberArray(response.payload.errors)) {
                    this.set('importErrors', response.payload.errors);
                } else {
                    this.set('importErrors', [{message: 'Import failed due to an unknown error. Check the Web Inspector console and network tabs for errors.'}]);
                }

                throw response;
            }).finally(() => {
                this.set('uploadButtonText', 'Import');
            });
        },

        downloadFile(endpoint) {
            let downloadURL = this.get('ghostPaths.url').api(endpoint);
            let iframe = $('#iframeDownload');

            if (iframe.length === 0) {
                iframe = $('<iframe>', {id: 'iframeDownload'}).hide().appendTo('body');
            }

            iframe.attr('src', downloadURL);
        },

        toggleDeleteAllModal() {
            this.toggleProperty('showDeleteAllModal');
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload x" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-setting-action')
                .find('input[type="file"]')
                .click();
        },

        setDefaultContentVisibility(value) {
            this.set('settings.defaultContentVisibility', value);
        },

        setSubscriptionSettings(key, event) {
            let subscriptionSettings = this.parseSubscriptionSettings(this.get('settings.membersSubscriptionSettings'));
            let stripeProcessor = subscriptionSettings.paymentProcessors.find((proc) => {
                return (proc.adapter === 'stripe');
            });
            let stripeConfig = stripeProcessor.config;
            stripeConfig.product = {
                name: this.settings.get('title')
            };
            // TODO: this flag has to be removed as it doesn't serve any purpose
            if (key === 'isPaid') {
                subscriptionSettings.isPaid = event;
            }
            if (key === 'secret_token' || key === 'public_token') {
                stripeConfig[key] = event.target.value;
            }
            if (key === 'month' || key === 'year') {
                stripeConfig.plans = stripeConfig.plans.map((plan) => {
                    if (key === plan.interval) {
                        plan.amount = event.target.value * 100;
                    }
                    return plan;
                });
            }
            this.set('settings.membersSubscriptionSettings', JSON.stringify(subscriptionSettings));
        }
    },

    parseSubscriptionSettings(settingsString) {
        try {
            return JSON.parse(settingsString);
        } catch (e) {
            return {
                isPaid: false,
                paymentProcessors: [{
                    adapter: 'stripe',
                    config: {
                        secret_token: '',
                        public_token: '',
                        product: {
                            name: this.settings.get('title')
                        },
                        plans: [
                            {
                                name: 'Monthly',
                                currency: 'usd',
                                interval: 'month',
                                amount: ''
                            },
                            {
                                name: 'Yearly',
                                currency: 'usd',
                                interval: 'year',
                                amount: ''
                            }
                        ]
                    }
                }]
            };
        }
    },

    // TODO: convert to ember-concurrency task
    _validate(file) {
        // Windows doesn't have mime-types for json files by default, so we
        // need to have some additional checking
        if (file.type === '') {
            // First check file extension so we can early return
            let [, extension] = (/(?:\.([^.]+))?$/).exec(file.name);

            if (!extension || extension.toLowerCase() !== 'json') {
                return RSVP.reject(new UnsupportedMediaTypeError());
            }

            return new Promise((resolve, reject) => {
                // Extension is correct, so check the contents of the file
                let reader = new FileReader();

                reader.onload = function () {
                    let {result} = reader;

                    try {
                        JSON.parse(result);

                        return resolve();
                    } catch (e) {
                        return reject(new UnsupportedMediaTypeError());
                    }
                };

                reader.readAsText(file);
            });
        }

        let accept = this.importMimeType;

        if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
            return RSVP.reject(new UnsupportedMediaTypeError());
        }

        return RSVP.resolve();
    },

    sendTestEmail: task(function* () {
        let notifications = this.notifications;
        let emailUrl = this.get('ghostPaths.url').api('mail', 'test');

        try {
            yield this.ajax.post(emailUrl);
            notifications.showAlert('Check your email for the test message.', {type: 'info', key: 'test-email.send.success'});
            return true;
        } catch (error) {
            notifications.showAPIError(error, {key: 'test-email:send'});
        }
    }).drop(),

    saveSettings: task(function* () {
        return yield this.settings.save();
    }).drop(),

    redirectUploadResult: task(function* (success) {
        this.set('redirectSuccess', success);
        this.set('redirectFailure', !success);

        yield timeout(config.environment === 'test' ? 100 : 5000);

        this.set('redirectSuccess', null);
        this.set('redirectFailure', null);
        return true;
    }).drop(),

    routesUploadResult: task(function* (success) {
        this.set('routesSuccess', success);
        this.set('routesFailure', !success);

        yield timeout(config.environment === 'test' ? 100 : 5000);

        this.set('routesSuccess', null);
        this.set('routesFailure', null);
        return true;
    }).drop(),

    reset() {
        this.set('importErrors', null);
        this.set('importSuccessful', false);
    }
});
<commit_msg>Added number check for Labs/Members price values<commit_after>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import Controller from '@ember/controller';
import RSVP from 'rsvp';
import config from 'ghost-admin/config/environment';
import {
    UnsupportedMediaTypeError,
    isRequestEntityTooLargeError,
    isUnsupportedMediaTypeError
} from 'ghost-admin/services/ajax';
import {computed} from '@ember/object';
import {isBlank} from '@ember/utils';
import {isArray as isEmberArray} from '@ember/array';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import isNumber from 'ghost-admin/utils/isNumber';

const {Promise} = RSVP;

const IMPORT_MIME_TYPES = [
    'application/json',
    'application/zip',
    'application/x-zip-compressed'
];

const JSON_EXTENSION = ['json'];
const JSON_MIME_TYPE = ['application/json'];

const YAML_EXTENSION = ['yaml'];
const YAML_MIME_TYPE = [
    'text/vnd.yaml',
    'application/vnd.yaml',
    'text/x-yaml',
    'application/x-yaml'
];

export default Controller.extend({
    ajax: service(),
    config: service(),
    feature: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),

    importErrors: null,
    importSuccessful: false,
    showDeleteAllModal: false,
    submitting: false,
    uploadButtonText: 'Import',

    importMimeType: null,
    jsonExtension: null,
    jsonMimeType: null,
    yamlExtension: null,
    yamlMimeType: null,
    init() {
        this._super(...arguments);
        this.importMimeType = IMPORT_MIME_TYPES;
        this.jsonExtension = JSON_EXTENSION;
        this.jsonMimeType = JSON_MIME_TYPE;
        this.yamlExtension = YAML_EXTENSION;
        this.yamlMimeType = YAML_MIME_TYPE;
    },

    subscriptionSettings: computed('settings.membersSubscriptionSettings', function () {
        let subscriptionSettings = this.parseSubscriptionSettings(this.get('settings.membersSubscriptionSettings'));
        let stripeProcessor = subscriptionSettings.paymentProcessors.find((proc) => {
            return (proc.adapter === 'stripe');
        });
        let monthlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'month');
        let yearlyPlan = stripeProcessor.config.plans.find(plan => plan.interval === 'year');
        monthlyPlan.dollarAmount = isNumber(monthlyPlan.amount) ? (monthlyPlan.amount / 100) : '';
        yearlyPlan.dollarAmount = isNumber(yearlyPlan.amount) ? (yearlyPlan.amount / 100) : '';
        stripeProcessor.config.plans = {
            monthly: monthlyPlan,
            yearly: yearlyPlan
        };
        subscriptionSettings.stripeConfig = stripeProcessor.config;
        return subscriptionSettings;
    }),

    actions: {
        onUpload(file) {
            let formData = new FormData();
            let notifications = this.notifications;
            let currentUserId = this.get('session.user.id');
            let dbUrl = this.get('ghostPaths.url').api('db');

            this.set('uploadButtonText', 'Importing');
            this.set('importErrors', null);
            this.set('importSuccessful', false);

            return this._validate(file).then(() => {
                formData.append('importfile', file);

                return this.ajax.post(dbUrl, {
                    data: formData,
                    dataType: 'json',
                    cache: false,
                    contentType: false,
                    processData: false
                });
            }).then((response) => {
                let store = this.store;

                this.set('importSuccessful', true);

                if (response.problems) {
                    this.set('importErrors', response.problems);
                }

                // Clear the store, so that all the new data gets fetched correctly.
                store.unloadAll();

                // NOTE: workaround for behaviour change in Ember 2.13
                // store.unloadAll has some async tendencies so we need to schedule
                // the reload of the current user once the unload has finished
                // https://github.com/emberjs/data/issues/4963
                run.schedule('destroy', this, () => {
                    // Reload currentUser and set session
                    this.set('session.user', store.findRecord('user', currentUserId));

                    // TODO: keep as notification, add link to view content
                    notifications.showNotification('Import successful.', {key: 'import.upload.success'});

                    // reload settings
                    return this.settings.reload().then((settings) => {
                        this.feature.fetch();
                        this.config.set('blogTitle', settings.get('title'));
                    });
                });
            }).catch((response) => {
                if (isUnsupportedMediaTypeError(response) || isRequestEntityTooLargeError(response)) {
                    this.set('importErrors', [response]);
                } else if (response && response.payload.errors && isEmberArray(response.payload.errors)) {
                    this.set('importErrors', response.payload.errors);
                } else {
                    this.set('importErrors', [{message: 'Import failed due to an unknown error. Check the Web Inspector console and network tabs for errors.'}]);
                }

                throw response;
            }).finally(() => {
                this.set('uploadButtonText', 'Import');
            });
        },

        downloadFile(endpoint) {
            let downloadURL = this.get('ghostPaths.url').api(endpoint);
            let iframe = $('#iframeDownload');

            if (iframe.length === 0) {
                iframe = $('<iframe>', {id: 'iframeDownload'}).hide().appendTo('body');
            }

            iframe.attr('src', downloadURL);
        },

        toggleDeleteAllModal() {
            this.toggleProperty('showDeleteAllModal');
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload x" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-setting-action')
                .find('input[type="file"]')
                .click();
        },

        setDefaultContentVisibility(value) {
            this.set('settings.defaultContentVisibility', value);
        },

        setSubscriptionSettings(key, event) {
            let subscriptionSettings = this.parseSubscriptionSettings(this.get('settings.membersSubscriptionSettings'));
            let stripeProcessor = subscriptionSettings.paymentProcessors.find((proc) => {
                return (proc.adapter === 'stripe');
            });
            let stripeConfig = stripeProcessor.config;
            stripeConfig.product = {
                name: this.settings.get('title')
            };
            // TODO: this flag has to be removed as it doesn't serve any purpose
            if (key === 'isPaid') {
                subscriptionSettings.isPaid = event;
            }
            if (key === 'secret_token' || key === 'public_token') {
                stripeConfig[key] = event.target.value;
            }
            if (key === 'month' || key === 'year') {
                stripeConfig.plans = stripeConfig.plans.map((plan) => {
                    if (key === plan.interval) {
                        plan.amount = event.target.value * 100;
                    }
                    return plan;
                });
            }
            this.set('settings.membersSubscriptionSettings', JSON.stringify(subscriptionSettings));
        }
    },

    parseSubscriptionSettings(settingsString) {
        try {
            return JSON.parse(settingsString);
        } catch (e) {
            return {
                isPaid: false,
                paymentProcessors: [{
                    adapter: 'stripe',
                    config: {
                        secret_token: '',
                        public_token: '',
                        product: {
                            name: this.settings.get('title')
                        },
                        plans: [
                            {
                                name: 'Monthly',
                                currency: 'usd',
                                interval: 'month',
                                amount: ''
                            },
                            {
                                name: 'Yearly',
                                currency: 'usd',
                                interval: 'year',
                                amount: ''
                            }
                        ]
                    }
                }]
            };
        }
    },

    // TODO: convert to ember-concurrency task
    _validate(file) {
        // Windows doesn't have mime-types for json files by default, so we
        // need to have some additional checking
        if (file.type === '') {
            // First check file extension so we can early return
            let [, extension] = (/(?:\.([^.]+))?$/).exec(file.name);

            if (!extension || extension.toLowerCase() !== 'json') {
                return RSVP.reject(new UnsupportedMediaTypeError());
            }

            return new Promise((resolve, reject) => {
                // Extension is correct, so check the contents of the file
                let reader = new FileReader();

                reader.onload = function () {
                    let {result} = reader;

                    try {
                        JSON.parse(result);

                        return resolve();
                    } catch (e) {
                        return reject(new UnsupportedMediaTypeError());
                    }
                };

                reader.readAsText(file);
            });
        }

        let accept = this.importMimeType;

        if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
            return RSVP.reject(new UnsupportedMediaTypeError());
        }

        return RSVP.resolve();
    },

    sendTestEmail: task(function* () {
        let notifications = this.notifications;
        let emailUrl = this.get('ghostPaths.url').api('mail', 'test');

        try {
            yield this.ajax.post(emailUrl);
            notifications.showAlert('Check your email for the test message.', {type: 'info', key: 'test-email.send.success'});
            return true;
        } catch (error) {
            notifications.showAPIError(error, {key: 'test-email:send'});
        }
    }).drop(),

    saveSettings: task(function* () {
        return yield this.settings.save();
    }).drop(),

    redirectUploadResult: task(function* (success) {
        this.set('redirectSuccess', success);
        this.set('redirectFailure', !success);

        yield timeout(config.environment === 'test' ? 100 : 5000);

        this.set('redirectSuccess', null);
        this.set('redirectFailure', null);
        return true;
    }).drop(),

    routesUploadResult: task(function* (success) {
        this.set('routesSuccess', success);
        this.set('routesFailure', !success);

        yield timeout(config.environment === 'test' ? 100 : 5000);

        this.set('routesSuccess', null);
        this.set('routesFailure', null);
        return true;
    }).drop(),

    reset() {
        this.set('importErrors', null);
        this.set('importSuccessful', false);
    }
});
<|endoftext|>


<commit_before>function MainAssistant(db) {
	this.db = db;
	this.podcastList_ctrl = undefined;
}

MainAssistant.prototype = {
	setup: function() {
		Ares.setupSceneAssistant(this);

		// Add event listeners for the database
		this.db.addEventListener(PodcastStorage.PodcastStartUpdate, this.podcastUpdating.bind(this));
		this.db.addEventListener(PodcastStorage.PodcastUpdateSuccess, this.podcastUpdateSuccess.bind(this));
		this.db.addEventListener(PodcastStorage.PodcastUpdateFailure, this.podcastUpdateFailure.bind(this));

		// Get reference to the list on the screen
		this.podcastList_ctrl = this.controller.get('podcastList');

		// Populate the list with the podcasts
		this.podcastListModel.items = this.db.getPodcastList();
		this.controller.modelChanged(this.podcastListModel);

		this.db.updatePodcasts();

	},
	cleanup: function() {
		Ares.cleanupSceneAssistant(this);
	}
};

// Event listener that is passed the key to the podcast that is
// about to update
MainAssistant.prototype.podcastUpdating = function(podcastKey) {
	Mojo.Log.info("[MainAssistant] Podcast %s updating.", podcastKey);
	this.controller.setupWidget("spinner_"+podcastKey, { spinnerSize: 'small' }, { spinning: true });
};

// Event listener that is passed the key to the podcast that
// has successfully updated
MainAssistant.prototype.podcastUpdateSuccess = function(podcastKey) {
	Mojo.Log.info("[MainAssistant] Podcast %s finished updating.", podcastKey);
	// Populate the list with the podcasts
	this.podcastListModel.items = this.db.getPodcastList();
	this.controller.modelChanged(this.podcastListModel);
};

// Event listener that is passed the key to the podcast that
// has not successfully updated
MainAssistant.prototype.podcastUpdateFailure = function(podcastKey) {

};<commit_msg>Trying spinner.<commit_after>function MainAssistant(db) {
	this.db = db;
	this.podcastList_ctrl = undefined;
}

MainAssistant.prototype = {
	setup: function() {
		Ares.setupSceneAssistant(this);

		// Add event listeners for the database
		this.db.addEventListener(PodcastStorage.PodcastStartUpdate, this.podcastUpdating.bind(this));
		this.db.addEventListener(PodcastStorage.PodcastUpdateSuccess, this.podcastUpdateSuccess.bind(this));
		this.db.addEventListener(PodcastStorage.PodcastUpdateFailure, this.podcastUpdateFailure.bind(this));

		// Get reference to the list on the screen
		this.podcastList_ctrl = this.controller.get('podcastList');

		// Populate the list with the podcasts
		this.podcastListModel.items = this.db.getPodcastList();
		this.controller.modelChanged(this.podcastListModel);

		this.db.updatePodcasts();

	},
	cleanup: function() {
		Ares.cleanupSceneAssistant(this);
	}
};

// Event listener that is passed the key to the podcast that is
// about to update
MainAssistant.prototype.podcastUpdating = function(podcastKey) {
	Mojo.Log.info("[MainAssistant] Podcast %s updating.", podcastKey);
	var spinnerAttr = {
	   spinnerSize: Mojo.Widget.spinnerSmall
	};

	var spinnerModel = {
	   spinning: true
	};

	// Turn on the spinner
	this.controller.setupWidget($("spinner_"+podcastKey), spinnerAttr, spinnerModel);
};

// Event listener that is passed the key to the podcast that
// has successfully updated
MainAssistant.prototype.podcastUpdateSuccess = function(podcastKey) {
	Mojo.Log.info("[MainAssistant] Podcast %s finished updating.", podcastKey);
	// Populate the list with the podcasts
	this.podcastListModel.items = this.db.getPodcastList();
	this.controller.modelChanged(this.podcastListModel);
};

// Event listener that is passed the key to the podcast that
// has not successfully updated
MainAssistant.prototype.podcastUpdateFailure = function(podcastKey) {

};<|endoftext|>