<commit_before>$(document).ready(function() {
  // TODO fix hack
  // Hack because having In Thread characters as a group in addition to Template groups
  // duplicates characters in the dropdown, and therefore multiple options are selected
  var selectd;
  $("#active_character option[selected]").each(function(){
    if (!selectd) selectd = this;
    $(this).prop("selected", false);
  });
  $(selectd).prop("selected", true);

  // Adding Select2 UI to relevant selects
  $("#post_board_id").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_section_id").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_privacy").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_post_viewer_ids").select2({
    width: '200px',
    minimumResultsForSearch: 20,
    placeholder: 'Choose user(s) to view this post'
  });

  $("#post_tag_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter tag(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return { q: term['term'] };
      },
    },
    width: '300px'
  });

  $("#post_setting_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter setting(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return {
          q: term['term'],
          t: 'setting',
        };
      },
    },
    width: '300px'
  });

  $("#post_warning_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter warning(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return {
          q: term['term'],
          t: 'warning',
        };
      },
    },
    width: '300px'
  });

  $("#active_character").select2({
    minimumResultsForSearch: 10,
    width: '100%',
  });

  // TODO fix hack
  // Resizes screennames to be slightly smaller if they're long for UI reasons
  $(".post-screenname").each(function (index) {
    if($(this).height() > 20) {
      $(this).css('font-size', "14px");
      if($(this).height() > 20 ) { $(this).css('font-size', "12px"); };
    }
  });

  // Hack to deal with Firefox's "helpful" caching of form values on soft refresh
  var nameInSelect = $("#active_character").children("optgroup").children(':selected').text();
  var nameInUI = $("#post-editor .post-character").text();
  var iconInUI = $("#current-icon").attr('src');
  var iconId = $("#reply_icon_id").val();
  var iconInForm = $("#"+iconId).attr('src');
  if (!nameInSelect.startsWith(nameInUI)) {
    var characterId = $("#reply_character_id").val();
    getAndSetCharacterData(characterId);
    setIconFromId(iconId); // Reset icon in case above changed it
  } else if (iconInUI != iconInForm) {
    setIconFromId(iconId); // Handle the case where just the icon was cached
  };

  // Bind both change() and keyup() in the icon keyword dropdown because Firefox doesn't
  // respect up/down key selections in a dropdown as a valid change() trigger
  $("#icon_dropdown").change(function() { setIconFromId($(this).val()); });
  $("#icon_dropdown").keyup(function() { setIconFromId($(this).val()); });

  if ($("#post_section_id").val() == '') { setSections(); }
  $("#post_board_id").change(function() { setSections(); });

  $('.view-button').click(function() {
    if(this.id == 'rtf') {
      $("#html").removeClass('selected');
      $("#editor_mode").val('rtf')
      $(this).addClass('selected');
      tinyMCE.execCommand('mceAddEditor', true, 'post_content');
      tinyMCE.execCommand('mceAddEditor', true, 'reply_content');
    } else if (this.id == 'html') {
      $("#rtf").removeClass('selected');
      $("#editor_mode").val('html')
      $(this).addClass('selected');
      tinyMCE.execCommand('mceRemoveEditor', false, 'post_content');
      tinyMCE.execCommand('mceRemoveEditor', false, 'reply_content');
    };
  });

  $("#post_privacy").change(function() {
    if($(this).val() == 2) { // TODO don't hardcode, should be PRIVACY_ACCESS
      $("#access_list").show();
    } else {
      $("#access_list").hide();
    }
  });

  $("#submit_button").click(function() {
    $("#preview_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#draft_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  $("#preview_button").click(function() {
    $("#submit_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#draft_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  $("#draft_button").click(function() {
    $("#submit_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#preview_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  if($(".gallery-icon").length > 1) {
    bindIcon();
    bindGallery();
  }

  $("#swap-icon").click(function () {
    $('#character-selector').toggle();
    $('html, body').scrollTop($("#post-editor").offset().top);
  });

  $("#active_character_chosen").click(function () {
    $('html, body').scrollTop($("#post-editor").offset().top);
  });

  $("#active_character").change(function() {
    // Set the ID
    var id = $(this).val();
    $("#reply_character_id").val(id);
    getAndSetCharacterData(id);
  });

  // Hides selectors when you hit the escape key
  $(document).bind("keydown", function(e){
    e = e || window.event;
    var charCode = e.which || e.keyCode;
    if(charCode == 27) {
      $('#icon-overlay').hide();
      $('#gallery').hide();
      $('#character-selector').hide();
    }
  });

  // Hides selectors when you click outside them
  $(document).click(function(e) {
    var target = e.target;

    if (!$(target).is('#current-icon-holder') &&
      !$(target).parents().is('#current-icon-holder') &&
      !$(target).is('#gallery') &&
      !$(target).parents().is('#gallery')) {
        $('#icon-overlay').hide();
        $('#gallery').hide();
    }

    if (!$(target).is('#character-selector') &&
      !$(target).is('#swap-icon') &&
      !$(target).parents().is('#character-selector')) {
        $('#character-selector').hide();
    }
  });
});

bindGallery = function() {
  $("#gallery img").click(function() {
    id = $(this).attr('id');
    setIconFromId(id, $(this));
  });
};

bindIcon = function() {
  $('#current-icon-holder').click(function() {
    $('#icon-overlay').toggle();
    $('#gallery').toggle();
    $('html, body').scrollTop($("#post-editor").offset().top);
  });
};

galleryString = function(gallery, multiGallery) {
  var iconsString = "";
  var icons = gallery["icons"];

  for (var i=0; i<icons.length; i++) {
    iconsString += iconString(icons[i]);
  }

  if(!multiGallery) { return iconsString; }

  var nameString = "
" + gallery['name'] + "
"
  return "
" + nameString + iconsString + "
"
};

iconString = function(icon) {
  var img_id = icon["id"];
  var img_url = icon["url"];
  var img_key = icon["keyword"];

  if (!icon['skip_dropdown']) $("#icon_dropdown").append($("<option>").attr({value: img_id}).append(img_key));
  var icon_img = $("").attr({src: img_url, id: img_id, alt: img_key, title: img_key, 'class': 'icon'});
  return $("
").attr('class', 'gallery-icon').append(icon_img).append("
").append(img_key)[0].outerHTML;
};

tinyMCESetup = function(ed) {
  ed.on('init', function(args) {
    if($("#html").hasClass('selected') == true) {
      tinyMCE.execCommand('mceRemoveEditor', false, 'post_content');
      tinyMCE.execCommand('mceRemoveEditor', false, 'reply_content');
      $(".tinymce").val(gon.original_content); // TODO fix hack
    } else {
      var rawContent = tinymce.activeEditor.getContent({format: 'raw'});
      var content = tinymce.activeEditor.getContent();
      if (rawContent == '

' && content == '') { tinymce.activeEditor.setContent(''); } // TODO fix hack
    };
  });
};

getAndSetCharacterData = function(characterId) {
  // Handle page interactions
  $("#character-selector").hide();
  $("#current-icon-holder").unbind();
  $("#icon_dropdown").empty().append('<option value="">No Icon</option>');

  // Handle special case where just setting to your base account
  if (characterId == '') {
    $("#post-editor .post-character").hide();
    $("#post-editor .post-screenname").hide();

    var url = gon.current_user.avatar.url;
    if(url != null) {
      var aid = gon.current_user.avatar.id;
      var keyword = gon.current_user.avatar.keyword;
      $("#gallery").html("");
      $("#gallery").append(iconString({id: aid, url: url, keyword: keyword}));
      $("#gallery").append(iconString({id: '', url: '/images/no-icon.png', keyword: 'No Icon', skip_dropdown: true}));
      bindIcon();
      bindGallery();
      setIcon(aid, url, keyword, keyword);
      $("#post-editor #post-author-spacer").show();
    } else {
      $("#post-editor #post-author-spacer").hide();
    }

    return // Don't need to load data from server (TODO combine with below?)
  }

  $.post(gon.character_path, {'character_id':characterId}, function (resp) {
    // Display the correct name/screenname fields
    $("#post-editor #post-author-spacer").hide();
    $("#post-editor .post-character").show().html(resp['name']);
    if(resp['screenname'] == undefined) {
      $("#post-editor .post-screenname").hide();
    } else {
      $("#post-editor .post-screenname").show().html(resp['screenname']);
    }

    // Display no icon if no default set
    if (resp['default'] == undefined) {
      $("#current-icon").removeClass('pointer');
      setIcon('');
      return;
    }

    // Display default icon
    $("#current-icon").addClass('pointer');

    // Calculate new galleries
    $("#gallery").html("");
    var galleries = resp['galleries'];
    var multiGallery = galleries.length > 1;
    for(var i=0; i<galleries.length; i++) {
      var gallery = galleries[i];
      $("#gallery").append(galleryString(gallery, multiGallery));
    }

    $("#gallery").append(iconString({id: '', url: '/images/no-icon.png', keyword: 'No Icon', skip_dropdown: true}));
    bindGallery();
    bindIcon();
    setIcon(resp['default']['id'], resp['default']['url'], resp['default']['keyword'], resp['default']['keyword']);
  });
};

setIconFromId = function(id, img) {
  // Assumes the #gallery div is populated with icons with the correct values
  if (id == "") return setIcon(id);
  if (typeof(img) === 'undefined') img = $("#"+id);
  setIcon(id, img.attr('src'), img.attr('title'), img.attr('alt'));
};

setIcon = function(id, url, title, alt) {
  // Handle No Icon case
  if (id == "") {
    url = "/images/no-icon.png";
    title = "No Icon";
    alt = "";
  }

  // Hide icon UI elements
  $('#icon-overlay').hide();
  $('#gallery').hide();

  // Set necessary form values
  $("#reply_icon_id").val(id);
  $("#icon_dropdown").val(id);

  // Set current icon UI elements
  $("#current-icon").attr('src', url);
  $("#current-icon").attr('title', title);
  $("#current-icon").attr('alt', alt);
};

setSections = function() {
  var board_id = $("#post_board_id").val();
  $.get("/boards/"+board_id, {}, function(resp) {
    if (resp.length > 0) {
      $("#section").show();
      $("#post_section_id").empty().append('<option value="">— Choose Section —</option>');
      for(var i = 0; i < resp.length; i++) {
        $("#post_section_id").append('<option value="'+resp[i][0]+'">'+resp[i][1]+'</option>');
      }
      $("#post_section_id").trigger("chosen:updated");
    } else {
      $("#post_section_id").val("");
      $("#section").hide();
    }
  });
};
<commit_msg>Fix event triggering<commit_after>$(document).ready(function() {
  // TODO fix hack
  // Hack because having In Thread characters as a group in addition to Template groups
  // duplicates characters in the dropdown, and therefore multiple options are selected
  var selectd;
  $("#active_character option[selected]").each(function(){
    if (!selectd) selectd = this;
    $(this).prop("selected", false);
  });
  $(selectd).prop("selected", true);

  // Adding Select2 UI to relevant selects
  $("#post_board_id").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_section_id").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_privacy").select2({
    width: '200px',
    minimumResultsForSearch: 20,
  });

  $("#post_post_viewer_ids").select2({
    width: '200px',
    minimumResultsForSearch: 20,
    placeholder: 'Choose user(s) to view this post'
  });

  $("#post_tag_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter tag(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return { q: term['term'] };
      },
    },
    width: '300px'
  });

  $("#post_setting_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter setting(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return {
          q: term['term'],
          t: 'setting',
        };
      },
    },
    width: '300px'
  });

  $("#post_warning_ids").select2({
    tags: true,
    tokenSeparators: [','],
    placeholder: 'Enter warning(s) separated by commas',
    ajax: {
      delay: 200,
      url: '/tags',
      dataType: 'json',
      data: function(term, page) {
        return {
          q: term['term'],
          t: 'warning',
        };
      },
    },
    width: '300px'
  });

  $("#active_character").select2({
    minimumResultsForSearch: 10,
    width: '100%',
  });

  // TODO fix hack
  // Resizes screennames to be slightly smaller if they're long for UI reasons
  $(".post-screenname").each(function (index) {
    if($(this).height() > 20) {
      $(this).css('font-size', "14px");
      if($(this).height() > 20 ) { $(this).css('font-size', "12px"); };
    }
  });

  // Hack to deal with Firefox's "helpful" caching of form values on soft refresh
  var nameInSelect = $("#active_character").children("optgroup").children(':selected').text();
  var nameInUI = $("#post-editor .post-character").text();
  var iconInUI = $("#current-icon").attr('src');
  var iconId = $("#reply_icon_id").val();
  var iconInForm = $("#"+iconId).attr('src');
  if (!nameInSelect.startsWith(nameInUI)) {
    var characterId = $("#reply_character_id").val();
    getAndSetCharacterData(characterId);
    setIconFromId(iconId); // Reset icon in case above changed it
  } else if (iconInUI != iconInForm) {
    setIconFromId(iconId); // Handle the case where just the icon was cached
  };

  // Bind both change() and keyup() in the icon keyword dropdown because Firefox doesn't
  // respect up/down key selections in a dropdown as a valid change() trigger
  $("#icon_dropdown").change(function() { setIconFromId($(this).val()); });
  $("#icon_dropdown").keyup(function() { setIconFromId($(this).val()); });

  if ($("#post_section_id").val() == '') { setSections(); }
  $("#post_board_id").change(function() { setSections(); });

  $('.view-button').click(function() {
    if(this.id == 'rtf') {
      $("#html").removeClass('selected');
      $("#editor_mode").val('rtf')
      $(this).addClass('selected');
      tinyMCE.execCommand('mceAddEditor', true, 'post_content');
      tinyMCE.execCommand('mceAddEditor', true, 'reply_content');
    } else if (this.id == 'html') {
      $("#rtf").removeClass('selected');
      $("#editor_mode").val('html')
      $(this).addClass('selected');
      tinyMCE.execCommand('mceRemoveEditor', false, 'post_content');
      tinyMCE.execCommand('mceRemoveEditor', false, 'reply_content');
    };
  });

  $("#post_privacy").change(function() {
    if($(this).val() == 2) { // TODO don't hardcode, should be PRIVACY_ACCESS
      $("#access_list").show();
    } else {
      $("#access_list").hide();
    }
  });

  $("#submit_button").click(function() {
    $("#preview_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#draft_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  $("#preview_button").click(function() {
    $("#submit_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#draft_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  $("#draft_button").click(function() {
    $("#submit_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    $("#preview_button").removeAttr('data-disable-with').attr('disabled', 'disabled');
    return true;
  });

  if($(".gallery-icon").length > 1) {
    bindIcon();
    bindGallery();
  }

  $("#swap-icon").click(function () {
    $('#character-selector').toggle();
    $('html, body').scrollTop($("#post-editor").offset().top);
  });

  $("#active_character").on('select2:close', function () {
    $('html, body').scrollTop($("#post-editor").offset().top);
  });

  $("#active_character").change(function() {
    // Set the ID
    var id = $(this).val();
    $("#reply_character_id").val(id);
    getAndSetCharacterData(id);
  });

  // Hides selectors when you hit the escape key
  $(document).bind("keydown", function(e){
    e = e || window.event;
    var charCode = e.which || e.keyCode;
    if(charCode == 27) {
      $('#icon-overlay').hide();
      $('#gallery').hide();
      $('#character-selector').hide();
    }
  });

  // Hides selectors when you click outside them
  $(document).click(function(e) {
    var target = e.target;

    if (!$(target).is('#current-icon-holder') &&
      !$(target).parents().is('#current-icon-holder') &&
      !$(target).is('#gallery') &&
      !$(target).parents().is('#gallery')) {
        $('#icon-overlay').hide();
        $('#gallery').hide();
    }

    if (!$(target).is('#character-selector') &&
      !$(target).is('#swap-icon') &&
      !$(target).parents().is('#character-selector')) {
        $('#character-selector').hide();
    }
  });
});

bindGallery = function() {
  $("#gallery img").click(function() {
    id = $(this).attr('id');
    setIconFromId(id, $(this));
  });
};

bindIcon = function() {
  $('#current-icon-holder').click(function() {
    $('#icon-overlay').toggle();
    $('#gallery').toggle();
    $('html, body').scrollTop($("#post-editor").offset().top);
  });
};

galleryString = function(gallery, multiGallery) {
  var iconsString = "";
  var icons = gallery["icons"];

  for (var i=0; i<icons.length; i++) {
    iconsString += iconString(icons[i]);
  }

  if(!multiGallery) { return iconsString; }

  var nameString = "
" + gallery['name'] + "
"
  return "
" + nameString + iconsString + "
"
};

iconString = function(icon) {
  var img_id = icon["id"];
  var img_url = icon["url"];
  var img_key = icon["keyword"];

  if (!icon['skip_dropdown']) $("#icon_dropdown").append($("<option>").attr({value: img_id}).append(img_key));
  var icon_img = $("").attr({src: img_url, id: img_id, alt: img_key, title: img_key, 'class': 'icon'});
  return $("
").attr('class', 'gallery-icon').append(icon_img).append("
").append(img_key)[0].outerHTML;
};

tinyMCESetup = function(ed) {
  ed.on('init', function(args) {
    if($("#html").hasClass('selected') == true) {
      tinyMCE.execCommand('mceRemoveEditor', false, 'post_content');
      tinyMCE.execCommand('mceRemoveEditor', false, 'reply_content');
      $(".tinymce").val(gon.original_content); // TODO fix hack
    } else {
      var rawContent = tinymce.activeEditor.getContent({format: 'raw'});
      var content = tinymce.activeEditor.getContent();
      if (rawContent == '

' && content == '') { tinymce.activeEditor.setContent(''); } // TODO fix hack
    };
  });
};

getAndSetCharacterData = function(characterId) {
  // Handle page interactions
  $("#character-selector").hide();
  $("#current-icon-holder").unbind();
  $("#icon_dropdown").empty().append('<option value="">No Icon</option>');

  // Handle special case where just setting to your base account
  if (characterId == '') {
    $("#post-editor .post-character").hide();
    $("#post-editor .post-screenname").hide();

    var url = gon.current_user.avatar.url;
    if(url != null) {
      var aid = gon.current_user.avatar.id;
      var keyword = gon.current_user.avatar.keyword;
      $("#gallery").html("");
      $("#gallery").append(iconString({id: aid, url: url, keyword: keyword}));
      $("#gallery").append(iconString({id: '', url: '/images/no-icon.png', keyword: 'No Icon', skip_dropdown: true}));
      bindIcon();
      bindGallery();
      setIcon(aid, url, keyword, keyword);
      $("#post-editor #post-author-spacer").show();
    } else {
      $("#post-editor #post-author-spacer").hide();
    }

    return // Don't need to load data from server (TODO combine with below?)
  }

  $.post(gon.character_path, {'character_id':characterId}, function (resp) {
    // Display the correct name/screenname fields
    $("#post-editor #post-author-spacer").hide();
    $("#post-editor .post-character").show().html(resp['name']);
    if(resp['screenname'] == undefined) {
      $("#post-editor .post-screenname").hide();
    } else {
      $("#post-editor .post-screenname").show().html(resp['screenname']);
    }

    // Display no icon if no default set
    if (resp['default'] == undefined) {
      $("#current-icon").removeClass('pointer');
      setIcon('');
      return;
    }

    // Display default icon
    $("#current-icon").addClass('pointer');

    // Calculate new galleries
    $("#gallery").html("");
    var galleries = resp['galleries'];
    var multiGallery = galleries.length > 1;
    for(var i=0; i<galleries.length; i++) {
      var gallery = galleries[i];
      $("#gallery").append(galleryString(gallery, multiGallery));
    }

    $("#gallery").append(iconString({id: '', url: '/images/no-icon.png', keyword: 'No Icon', skip_dropdown: true}));
    bindGallery();
    bindIcon();
    setIcon(resp['default']['id'], resp['default']['url'], resp['default']['keyword'], resp['default']['keyword']);
  });
};

setIconFromId = function(id, img) {
  // Assumes the #gallery div is populated with icons with the correct values
  if (id == "") return setIcon(id);
  if (typeof(img) === 'undefined') img = $("#"+id);
  setIcon(id, img.attr('src'), img.attr('title'), img.attr('alt'));
};

setIcon = function(id, url, title, alt) {
  // Handle No Icon case
  if (id == "") {
    url = "/images/no-icon.png";
    title = "No Icon";
    alt = "";
  }

  // Hide icon UI elements
  $('#icon-overlay').hide();
  $('#gallery').hide();

  // Set necessary form values
  $("#reply_icon_id").val(id);
  $("#icon_dropdown").val(id);

  // Set current icon UI elements
  $("#current-icon").attr('src', url);
  $("#current-icon").attr('title', title);
  $("#current-icon").attr('alt', alt);
};

setSections = function() {
  var board_id = $("#post_board_id").val();
  $.get("/boards/"+board_id, {}, function(resp) {
    if (resp.length > 0) {
      $("#section").show();
      $("#post_section_id").empty().append('<option value="">— Choose Section —</option>');
      for(var i = 0; i < resp.length; i++) {
        $("#post_section_id").append('<option value="'+resp[i][0]+'">'+resp[i][1]+'</option>');
      }
      $("#post_section_id").trigger("change");
    } else {
      $("#post_section_id").val("");
      $("#section").hide();
    }
  });
};
<|endoftext|>


<commit_before>function nextPost() {
  $(".js-next").on("click", function(event) {
    event.preventDefault();
    var nextId = parseInt($(".js-next").attr("data-id")) + 1;
    $.get("/posts/" + nextId + ".json", function(post) {
      // get post
      $(".postTitle").text(post.title);
      $(".postContent").text(post.content);
      // re-set the id to current on the link
      $(".js-next").attr("data-id", post.id);
      renderComments(post.comments);
    }).fail(function() {
      $('#flash').append("
Sorry, there are more posts on the way!
");
    });
  });
}

function renderComments(comments) {
  $('.comments').empty()
  $.each(comments, function(index, comment){
    if (comment.user != null) {
      $('.comments').append(
        "
" + comment['user'].first_name + " " + comment['user'].last_name + " says:
",
        "
" + comment.content + "
"
      );
    }
  });
}
<commit_msg>simple refactor on the ajax call to extract away some html appending<commit_after>function nextPost() {
  $(".js-next").on("click", function(event) {
    event.preventDefault();
    var nextId = parseInt($(".js-next").attr("data-id")) + 1;
    $.get("/posts/" + nextId + ".json", function(post) {
      renderPost(post);
      renderComments(post.comments);
    }).fail(function() {
      $('#flash').append("
Sorry, there are more posts on the way!
");
    });
  });
}

function renderComments(comments) {
  $('.comments').empty()
  $.each(comments, function(index, comment){
    if (comment.user != null) {
      $('.comments').append(
        "
" + comment['user'].first_name + " " + comment['user'].last_name + " says:
",
        "
" + comment.content + "
"
      );
    }
  });
}

function renderPost(post) {
  $(".postTitle").text(post.title);
  $(".postContent").text(post.content);
  // re-set the id to current on the link
  $(".js-next").attr("data-id", post.id);
}
<|endoftext|>


<commit_before>////////////////////////////////////////
// Get the user's id
////////////////////////////////////////

$(function() {
  user_name = $("#main_menu").attr("class");

});

////////////////////////////////////////
// heartbeat
////////////////////////////////////////

//$(function() {
//  if ($('#content').length > 0) {
//    setTimeout(heartbeat, 20000);
//  }
//});

var heartbeat = function() {
  // store the current user's name
  user_id = $("#content").attr("class");

    $.ajax( {
      type: "PUT",
      url: "/users/" + user_id + ".js",
      success: function(response) {
        response;
      }
    });

    setTimeout(heartbeat, 20000);
};

////////////////////////////////////////
// Calculate Height
////////////////////////////////////////

// calculate_message_section_height
// resize the '#messages_section' to be 68% of the window
var calculate_message_section_height = function() {
  height = $(window).outerHeight(true);
  calculated_height = (height * 68) / 100;
  $('#messages_section').height(calculated_height);
  //$('#messages_section').scrollTo("max");
};

// resize on page load
$(function() {
  calculate_message_section_height();
});

// resize on each browser resize by the user
$(function() {
  $(window).resize(calculate_message_section_height);
});

////////////////////////////////////////
// hide_available_users()
////////////////////////////////////////

var hide_available_users = function() {
  $("#recipient_selection_section").html("Add Available Users");
};

////////////////////////////////////////
// auto select jobs on signin page
////////////////////////////////////////

$(function() {
  // if on the sign on page
  if ($("#signin").length > 0) {
    // user name field loses focus
    $("input#user_name").focusout(function(event) {
      name = event.target.value;
      if (name) {
        // GET - sessions#new
        $.ajax( {
          type: "GET",
          url: "/session/new.json",
          data: { "user": name },
          success: function(response) {
            if (response.normal_workstations) {
              $("input:checkbox").removeAttr("checked");
              var normal_workstations = response.normal_workstations.split(",");
              var i;
              for (i = 0; i < normal_workstations.length; i++) {
                $("input#" + normal_workstations[i]).prop("checked", true);
              }
            }
          }
        });
      }
    });
  }
});

/////////////////////////////////////////
// create the workstation html elements
/////////////////////////////////////////

// get the workstation data
var build_workstation_buttons = function() {
  var workstation_section = $('#recipient_workstation_selection');
  if (workstation_section.length > 0) {
    $.ajax( {
      type: "GET",
      url: "/workstations.json",
      success: function(response) {
        for (var i = 0; i < response.length; i++) {
          var html = "
";
          html += "
" + response[i].long_name + "
";
          if (response[i].user_name && response[i].user_name.length > 0) {
            html += "
(" + response[i].user_name + ")
";
          } else {
            html += "
(vacant)
"
          }
          html += "
";
          var workstation = $(html).data("workstation_id", response[i].id).turnOff().click(toggle_recipient);
          workstation_section.append(workstation);
        }
      }
    });
  }
};

// get the user data
var build_user_workstation_info = function() {
  var workstation_section = $('#recipient_workstation_selection');
  var user_name = $("#main_menu").attr("class");
  if (workstation_section.length > 0) {
    $.ajax( {
      type: "GET",
      url: "/users/" + user_name + ".json",
      success: function(response) {
        for (var i = 0; i < response.workstations.length; i++) {
          $("#" + response.workstations[i].name).addClass("mine").removeClass("off");
        }
        for (var i = 0; i < response.recipient_workstations.length; i++) {
          $("#" + response.recipient_workstations[i].name).turnOn().data("recipient_id", response.recipient_workstations[i].recipient_id);
        }
      }
    });
  }
};

$(build_workstation_buttons);
$(build_user_workstation_info);

///////////////////////////////////
// add recipient
///////////////////////////////////

var toggle_recipient = function() {
  var state = $(this).onOrOff();

  if (state && state == "off") {
    var workstation_id = $(this).data("workstation_id");

    if (workstation_id) {
      // POST - recipients#create
      $.ajax( {
        type: "POST",
        url: "/recipients",
        data: { "workstation_id": workstation_id },
        success: function(response) {
          response;
        }
      });
    }

  } else if (state && state == "on") {
    var recipient_id = $(this).data("recipient_id");

    if (recipient_id) {
      // DELETE - recipients#destroy
      $.ajax( {
        type: "POST",
        url: "/recipients/" + recipient_id,
        data: { _method: 'delete' },
        success: function(response) {
          response;
        }
      });
    }
  }
};

///////////////////////////////////
// Toggle all (workstations)
//////////////////////////////////

var toggle_all_workstations = function() {
  $(this).toggleClass("all");
  $(this).toggleClass("none");

  if ($(this).hasClass("none")) {
    $(this).html("
Message
none
");

    // POST - recipients#create all
    $.ajax( {
      type: "POST",
      url: "/recipients",
      data: { "workstation_id": "all" },
      success: function(response) {
        response;
      }
    });
  } else {
    $(this).html("
Message
all
");

    // DELETE - workstations#destroy all
    $.ajax( {
      type: "POST",
      url: "/recipients/all",
      data: { _method: 'delete' },
      success: function(response) {
        response;
      }
    });
  }
}

$(function() {
  $("#toggle_all_workstations").click(toggle_all_workstations);
});

///////////////////////////////////
// acknowledge (read) message
///////////////////////////////////

var read_message = function(message_id) {
  var classes = $(this).attr("class").split(" ");
  var i;
  var message_id = 0;
  for (i = 0; i < classes.length; i += 1) {
    if (classes[i].indexOf("msg-") != -1) {
      var klas = classes[i].substring(4, classes[i].length);
      message_id = parseInt(klas);
    }
  }

  if (message_id) {
    $.ajax( {
      type: "PUT",
      url: "/messages/" + message_id,
      success: function(response) {
        response;
      }
    });
  }
};

$(function() {
  $("li.message.recieved.unread").click(read_message);
});

////////////////////////////////////
// create a user_name
////////////////////////////////////

var name_user = function() {
  var user_name_input = $('input#user_user_name');
  if (!user_name_input.val()) {
    var first_name = $('input#user_first_name').val();
    var middle_initial = $('input#user_middle_initial').val();
    var last_name = $('input#user_last_name').val();
    var user_name = "";
    if (first_name && first_name.length >= 1) {
      user_name += first_name[0];
    }
    if (middle_initial && middle_initial.length >= 1) {
      user_name += middle_initial[0];
    }
    if (last_name && last_name.length >= 1) {
      user_name += last_name;
    }
    if (user_name) {
      user_name_input.val(user_name.toLowerCase());
    }
  }
};

$(function() {
  $('input#user_user_name').click(name_user).focusin(name_user);
  $('label[for=user_user_name]').click(name_user);
});

////////////////////////////////////
// Workstation radio buttons
////////////////////////////////////
$(function() {
  $("#td_workstations input").click(function() {
    $("#ops_workstations input").removeAttr("checked");
  });
  $("input#YDCTL").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDMSTR").removeAttr("checked");
    $("input#GLHSE").removeAttr("checked");
  });
  $("input#YDMSTR").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDCTL").removeAttr("checked");
    $("input#GLHSE").removeAttr("checked");
  });
  $("input#GLHSE").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDCTL").removeAttr("checked");
    $("input#YDMSTR").removeAttr("checked");
  });
});

///////////////////////////////////
// show the most recent message
///////////////////////////////////

//$(function() {
//  if ($("#messages_section").length > 0) {
//    $('#messages_section').scrollTo("max");
//  }
//});

///////////////////////////////////
// Upload Section
///////////////////////////////////

$(function() {
  $('a#attach_button').click(function() {
    upload_section = $('#upload_section');
    height = upload_section.outerHeight(true);
    messages_section = $('#messages_section');
    upload_section.toggle();
    upload_button = $('a#attach_button');

    if (upload_section.is(":hidden")) {
      messages_section.css("height", "+=" + height);
      $('input[type="file"]').val("");
      upload_button.text("Show File Uploader");
    } else {
      messages_section.css("height", "-=" + height);
      upload_button.text("Hide File Uploader");
    }
  });
});

//////////////////////////////////////////////
// message upload
//////////////////////////////////////////////

//$(function() {
//  if ($("#messages_section").length > 0) {
//    var uploader = new qq.FileUploader({
//        // pass the dom node (ex. $(selector)[0] for jQuery users)
//        element: document.getElementById('file-uploader'),
//        // path to server-side upload script
//        action: '/attachments.js'
//    });
//  }
//});

///////////////////////////////////////////////
// message recieve handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/messages/" + user_name, function(data, channel) {
    // play tone
    $('#tone')[0].innerHTML = "<embed src=/assets/soft_chime_beep.mp3 hidden=true autostart=true loop=false>";

    // clear message text field
    $("input#message_content").val("");

    // create the new message html
    var html ="
" +
            "
" +
              "
" +
                "
" +
                  "
" + data.sender + "
" +
                "
" +
                "
" +
                  "
" + data.timestamp + "
" +
                "
" +
              "
" +
              "
" +
                "
";

    if (data.attachment_url) {
      html += "" + data.chat_message + "";
    } else {
      html += "
" + data.chat_message + "
";
    }

    html += "
" +
           "
" +
         "
" +
       "
";

    // display the new message
    display_new_message(html);

    // add click handler to new message element
    $("li.message.recieved.unread").click(read_message);

    // show the sending workstation(s) as recipient(s)
    for (var i = 0; i < data.recipient_ids.length; i++) {
      if (data.recipient_ids[i] > 0) {
        selector = "#" + data.from_workstations[i] + ".recipient_workstation.off";
        $(selector).removeClass("off").addClass(data.recipient_ids[i].toString()).addClass("on");
      }
    }

    // scroll to last message
    //if (data.chat_message) {
    //  $('#messages_section').scrollTo("max");
    //}
  });
});

var display_new_message = function(message_html) {
  message_list_item = $("li.message:first-child");
  if (message_list_item[0]) {
    message_list_item.before(message_html);
  } else {
    $("ul#message_list").html(message_html);
  }
};

///////////////////////////////////////////////
// user signin/signout handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/workstations/" + user_name, function(data, channel) {
    var workstations = data.workstations.split(",");
    for (var i = 0; i < workstations.length; i++) {
      selector = "#user_at_" + workstations[i];
      $(selector).html("(" + data.name + ")");
    }
  });
});

///////////////////////////////////////////////
// message read handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/readers/" + user_name, function(data, channel) {
    console.log("message: " + data.message);
    selector = "li.message.owner." + data.message + " ul.inner_message li .read_by";
    console.log(selector);
    $(selector).html(data.readers);
  });
});
<commit_msg>mark vacant workstations with a span id<commit_after>////////////////////////////////////////
// Get the user's id
////////////////////////////////////////

$(function() {
  user_name = $("#main_menu").attr("class");

});

////////////////////////////////////////
// heartbeat
////////////////////////////////////////

//$(function() {
//  if ($('#content').length > 0) {
//    setTimeout(heartbeat, 20000);
//  }
//});

var heartbeat = function() {
  // store the current user's name
  user_id = $("#content").attr("class");

    $.ajax( {
      type: "PUT",
      url: "/users/" + user_id + ".js",
      success: function(response) {
        response;
      }
    });

    setTimeout(heartbeat, 20000);
};

////////////////////////////////////////
// Calculate Height
////////////////////////////////////////

// calculate_message_section_height
// resize the '#messages_section' to be 68% of the window
var calculate_message_section_height = function() {
  height = $(window).outerHeight(true);
  calculated_height = (height * 68) / 100;
  $('#messages_section').height(calculated_height);
  //$('#messages_section').scrollTo("max");
};

// resize on page load
$(function() {
  calculate_message_section_height();
});

// resize on each browser resize by the user
$(function() {
  $(window).resize(calculate_message_section_height);
});

////////////////////////////////////////
// hide_available_users()
////////////////////////////////////////

var hide_available_users = function() {
  $("#recipient_selection_section").html("Add Available Users");
};

////////////////////////////////////////
// auto select jobs on signin page
////////////////////////////////////////

$(function() {
  // if on the sign on page
  if ($("#signin").length > 0) {
    // user name field loses focus
    $("input#user_name").focusout(function(event) {
      name = event.target.value;
      if (name) {
        // GET - sessions#new
        $.ajax( {
          type: "GET",
          url: "/session/new.json",
          data: { "user": name },
          success: function(response) {
            if (response.normal_workstations) {
              $("input:checkbox").removeAttr("checked");
              var normal_workstations = response.normal_workstations.split(",");
              var i;
              for (i = 0; i < normal_workstations.length; i++) {
                $("input#" + normal_workstations[i]).prop("checked", true);
              }
            }
          }
        });
      }
    });
  }
});

/////////////////////////////////////////
// create the workstation html elements
/////////////////////////////////////////

// get the workstation data
var build_workstation_buttons = function() {
  var workstation_section = $('#recipient_workstation_selection');
  if (workstation_section.length > 0) {
    $.ajax( {
      type: "GET",
      url: "/workstations.json",
      success: function(response) {
        for (var i = 0; i < response.length; i++) {
          var html = "
";
          html += "
" + response[i].long_name + "
";
          if (response[i].user_name && response[i].user_name.length > 0) {
            html += "
(" + response[i].user_name + ")
";
          } else {
            html += "
(vacant)
"
          }
          html += "
";
          var workstation = $(html).data("workstation_id", response[i].id).turnOff().click(toggle_recipient);
          workstation_section.append(workstation);
        }
      }
    });
  }
};

// get the user data
var build_user_workstation_info = function() {
  var workstation_section = $('#recipient_workstation_selection');
  var user_name = $("#main_menu").attr("class");
  if (workstation_section.length > 0) {
    $.ajax( {
      type: "GET",
      url: "/users/" + user_name + ".json",
      success: function(response) {
        for (var i = 0; i < response.workstations.length; i++) {
          $("#" + response.workstations[i].name).addClass("mine").removeClass("off");
        }
        for (var i = 0; i < response.recipient_workstations.length; i++) {
          $("#" + response.recipient_workstations[i].name).turnOn().data("recipient_id", response.recipient_workstations[i].recipient_id);
        }
      }
    });
  }
};

$(build_workstation_buttons);
$(build_user_workstation_info);

///////////////////////////////////
// add recipient
///////////////////////////////////

var toggle_recipient = function() {
  var state = $(this).onOrOff();

  if (state && state == "off") {
    var workstation_id = $(this).data("workstation_id");

    if (workstation_id) {
      // POST - recipients#create
      $.ajax( {
        type: "POST",
        url: "/recipients",
        data: { "workstation_id": workstation_id },
        success: function(response) {
          response;
        }
      });
    }

  } else if (state && state == "on") {
    var recipient_id = $(this).data("recipient_id");

    if (recipient_id) {
      // DELETE - recipients#destroy
      $.ajax( {
        type: "POST",
        url: "/recipients/" + recipient_id,
        data: { _method: 'delete' },
        success: function(response) {
          response;
        }
      });
    }
  }
};

///////////////////////////////////
// Toggle all (workstations)
//////////////////////////////////

var toggle_all_workstations = function() {
  $(this).toggleClass("all");
  $(this).toggleClass("none");

  if ($(this).hasClass("none")) {
    $(this).html("
Message
none
");

    // POST - recipients#create all
    $.ajax( {
      type: "POST",
      url: "/recipients",
      data: { "workstation_id": "all" },
      success: function(response) {
        response;
      }
    });
  } else {
    $(this).html("
Message
all
");

    // DELETE - workstations#destroy all
    $.ajax( {
      type: "POST",
      url: "/recipients/all",
      data: { _method: 'delete' },
      success: function(response) {
        response;
      }
    });
  }
}

$(function() {
  $("#toggle_all_workstations").click(toggle_all_workstations);
});

///////////////////////////////////
// acknowledge (read) message
///////////////////////////////////

var read_message = function(message_id) {
  var classes = $(this).attr("class").split(" ");
  var i;
  var message_id = 0;
  for (i = 0; i < classes.length; i += 1) {
    if (classes[i].indexOf("msg-") != -1) {
      var klas = classes[i].substring(4, classes[i].length);
      message_id = parseInt(klas);
    }
  }

  if (message_id) {
    $.ajax( {
      type: "PUT",
      url: "/messages/" + message_id,
      success: function(response) {
        response;
      }
    });
  }
};

$(function() {
  $("li.message.recieved.unread").click(read_message);
});

////////////////////////////////////
// create a user_name
////////////////////////////////////

var name_user = function() {
  var user_name_input = $('input#user_user_name');
  if (!user_name_input.val()) {
    var first_name = $('input#user_first_name').val();
    var middle_initial = $('input#user_middle_initial').val();
    var last_name = $('input#user_last_name').val();
    var user_name = "";
    if (first_name && first_name.length >= 1) {
      user_name += first_name[0];
    }
    if (middle_initial && middle_initial.length >= 1) {
      user_name += middle_initial[0];
    }
    if (last_name && last_name.length >= 1) {
      user_name += last_name;
    }
    if (user_name) {
      user_name_input.val(user_name.toLowerCase());
    }
  }
};

$(function() {
  $('input#user_user_name').click(name_user).focusin(name_user);
  $('label[for=user_user_name]').click(name_user);
});

////////////////////////////////////
// Workstation radio buttons
////////////////////////////////////
$(function() {
  $("#td_workstations input").click(function() {
    $("#ops_workstations input").removeAttr("checked");
  });
  $("input#YDCTL").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDMSTR").removeAttr("checked");
    $("input#GLHSE").removeAttr("checked");
  });
  $("input#YDMSTR").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDCTL").removeAttr("checked");
    $("input#GLHSE").removeAttr("checked");
  });
  $("input#GLHSE").click(function() {
    $("#td_workstations input").removeAttr("checked");
    $("input#YDCTL").removeAttr("checked");
    $("input#YDMSTR").removeAttr("checked");
  });
});

///////////////////////////////////
// show the most recent message
///////////////////////////////////

//$(function() {
//  if ($("#messages_section").length > 0) {
//    $('#messages_section').scrollTo("max");
//  }
//});

///////////////////////////////////
// Upload Section
///////////////////////////////////

$(function() {
  $('a#attach_button').click(function() {
    upload_section = $('#upload_section');
    height = upload_section.outerHeight(true);
    messages_section = $('#messages_section');
    upload_section.toggle();
    upload_button = $('a#attach_button');

    if (upload_section.is(":hidden")) {
      messages_section.css("height", "+=" + height);
      $('input[type="file"]').val("");
      upload_button.text("Show File Uploader");
    } else {
      messages_section.css("height", "-=" + height);
      upload_button.text("Hide File Uploader");
    }
  });
});

//////////////////////////////////////////////
// message upload
//////////////////////////////////////////////

//$(function() {
//  if ($("#messages_section").length > 0) {
//    var uploader = new qq.FileUploader({
//        // pass the dom node (ex. $(selector)[0] for jQuery users)
//        element: document.getElementById('file-uploader'),
//        // path to server-side upload script
//        action: '/attachments.js'
//    });
//  }
//});

///////////////////////////////////////////////
// message recieve handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/messages/" + user_name, function(data, channel) {
    // play tone
    $('#tone')[0].innerHTML = "<embed src=/assets/soft_chime_beep.mp3 hidden=true autostart=true loop=false>";

    // clear message text field
    $("input#message_content").val("");

    // create the new message html
    var html ="
" +
            "
" +
              "
" +
                "
" +
                  "
" + data.sender + "
" +
                "
" +
                "
" +
                  "
" + data.timestamp + "
" +
                "
" +
              "
" +
              "
" +
                "
";

    if (data.attachment_url) {
      html += "" + data.chat_message + "";
    } else {
      html += "
" + data.chat_message + "
";
    }

    html += "
" +
           "
" +
         "
" +
       "
";

    // display the new message
    display_new_message(html);

    // add click handler to new message element
    $("li.message.recieved.unread").click(read_message);

    // show the sending workstation(s) as recipient(s)
    for (var i = 0; i < data.recipient_ids.length; i++) {
      if (data.recipient_ids[i] > 0) {
        selector = "#" + data.from_workstations[i] + ".recipient_workstation.off";
        $(selector).removeClass("off").addClass(data.recipient_ids[i].toString()).addClass("on");
      }
    }

    // scroll to last message
    //if (data.chat_message) {
    //  $('#messages_section').scrollTo("max");
    //}
  });
});

var display_new_message = function(message_html) {
  message_list_item = $("li.message:first-child");
  if (message_list_item[0]) {
    message_list_item.before(message_html);
  } else {
    $("ul#message_list").html(message_html);
  }
};

///////////////////////////////////////////////
// user signin/signout handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/workstations/" + user_name, function(data, channel) {
    var workstations = data.workstations.split(",");
    for (var i = 0; i < workstations.length; i++) {
      var selector = "#user_at_" + workstations[i];
      $(selector).html("(" + data.name + ")");
    }
  });
});

///////////////////////////////////////////////
// message read handler
///////////////////////////////////////////////

$(function() {
  // store the current user's name
  user_name = $("#main_menu").attr("class");

  // register callback
  PrivatePub.subscribe("/readers/" + user_name, function(data, channel) {
    console.log("message: " + data.message);
    selector = "li.message.owner." + data.message + " ul.inner_message li .read_by";
    console.log(selector);
    $(selector).html(data.readers);
  });
});
<|endoftext|>


<commit_before>$(function() {
    $("a.load_reviews").on("click", function(e){
//Send AJAX HIGH-LEVEL get request
  $.get(this.href).success(function(json) {
    var $ul = $("div.reviews ul")
    $ul.html(" ")
    json.forEach(function(review) {
      $ul.append("<li>" + review + "</li>");
    });
  });
 //Load response into HTML of page
  e.preventDefault();
 });
});
<commit_msg>Delete users.js no longer needed<commit_after><|endoftext|>


<commit_before>Template.homes.created = function () {
  // Get reference to template instance
  const templateInstance = this;

  // Subscribe to all homes
  templateInstance.subscribe('allHomes');

  // Subscribe to all groups
  templateInstance.subscribe('allGroups');
};

Template.homes.events({
  'click #new-group': function () {
    // Show the group modal (create new)
    Modal.show('groupModal');
  }
});

Template.homes.helpers({
  'groups': function () {
    // Get all Groups
    return Groups.find();
  }
});
<commit_msg>Subscribe to currentUserGroups<commit_after>Template.homes.created = function() {
  // Get reference to template instance
  const templateInstance = this;

  // Subscribe to all homes
  templateInstance.subscribe("allHomes");

  // Subscribe to all groups
  templateInstance.subscribe("currentUserGroups");
};

Template.homes.events({
  "click #new-group": function() {
    // Show the group modal (create new)
    Modal.show("groupModal");
  }
});

Template.homes.helpers({
  groups: function() {
    // Get all Groups
    return Groups.find();
  }
});
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React, { Component } from 'react';
import Joyride from 'react-joyride';
import i18n from '../services/i18n';

class AppOnboarding extends Component {
  state = {
    run: false
  };

  componentDidMount() {
    setTimeout(() => {
      this.setState({
        run: true
      });
    }, 1000);
  }

  callback = (data) => {
    const { action, index, type } = data;
  };

  render() {
    const { run } = this.state;
    const steps = [
      {
        target: '[data-tid=createNewLocation]',
        content: i18n.t('welcomeContent'),
        placement: 'bottom',
      },
      {
        target: '[data-tid=aboutTagSpaces]',
        content: 'Hello world',
        placement: 'bottom',
      },
    ];
    return (
      <Joyride
        steps={steps}
        run={run}
        callback={this.callback}
      />
    );
  }
}

export default AppOnboarding;
<commit_msg>disabling app onboarding<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React, { Component } from 'react';
import Joyride from 'react-joyride';
import i18n from '../services/i18n';

class AppOnboarding extends Component {
  state = {
    run: false
  };

  /* componentDidMount() {
    setTimeout(() => {
      this.setState({
        run: true
      });
    }, 1000);
  } */

  callback = (data) => {
    const { action, index, type } = data;
  };

  render() {
    const { run } = this.state;
    const steps = [
      {
        target: '[data-tid=createNewLocation]',
        content: i18n.t('welcomeContent'),
        placement: 'bottom',
      },
      {
        target: '[data-tid=aboutTagSpaces]',
        content: 'Hello world',
        placement: 'bottom',
      },
    ];
    return (
      <Joyride
        steps={steps}
        run={run}
        callback={this.callback}
      />
    );
  }
}

export default AppOnboarding;
<|endoftext|>


<commit_before>require("../../test-env");

function edgeLabel(g) {
  return function(u, v) {
    return g.hasEdge(u, v) ? g.edgeLabel(u, v) : Number.POSITIVE_INFINITY;
  }
}

describe("dig.alg.prim", function() {
  it("returns an empty graph given an empty graph", function() {
    var g = new dig.UGraph();
    assert.graphEqual(g, dig.alg.prim(g));
  });

  it("returns a single node for a singleton graph", function() {
    var g = new dig.UGraph();
    g.addNode(1);
    assert.graphEqual(g, dig.alg.prim(g));
  });

  it("returns a unique result for diamond graph with different path weights", function() {
    var g = new dig.UGraph();
    g.addNodes(1, 2, 3, 4);
    g.addEdge(1, 2, 1);
    g.addEdge(1, 3, 2);
    g.addEdge(2, 4, 3);
    g.addEdge(3, 4, 4);

    var expected = new dig.UGraph();
    expected.addNodes(1, 2, 3, 4);
    expected.addPath(1, 2, 4);
    expected.addEdge(1, 3);

    assert.graphEqual(expected, dig.alg.prim(g, edgeLabel(g)));
  });

  it("returns a unique result for a small network with different path weights", function() {
    var g = new dig.UGraph();
    g.addNodes(1, 2, 3, 4, 5, 6);
    g.addEdge(1, 2, 1);
    g.addEdge(1, 3, 3);
    g.addEdge(1, 6, 5);
    g.addEdge(2, 3, 9);
    g.addEdge(2, 4, 2);
    g.addEdge(3, 4, 8);
    g.addEdge(3, 5, 4);
    g.addEdge(3, 6, 6);
    g.addEdge(4, 5, 7);

    var expected = new dig.UGraph();
    expected.addNodes(1, 2, 3, 4, 5, 6);
    expected.addPath(1, 2, 4);
    expected.addPath(1, 3, 5);
    expected.addEdge(1, 6);

    assert.graphEqual(expected, dig.alg.prim(g, edgeLabel(g)));
  });
});
<commit_msg>Use dot graphs for prim's tests<commit_after>require("../../test-env");

graphs = require("../../test-graphs");

function edgeLabel(g) {
  return function(u, v) {
    return g.hasEdge(u, v) ? g.edgeLabel(u, v) : Number.POSITIVE_INFINITY;
  }
}

describe("dig.alg.prim", function() {
  it("returns an empty graph given an empty graph", function() {
    var g = graphs.undirected.empty;
    assert.graphEqual(g, dig.alg.prim(g));
  });

  it("returns a single node for a singleton graph", function() {
    var g = graphs.undirected.node1;
    assert.graphEqual(g, dig.alg.prim(g));
  });

  it("returns a unique result for a small network with different path weights", function() {
    var g = graphs.undirected.prim6;

    var expected = new dig.UGraph();
    expected.addNodes("A", "B", "C", "D", "E", "F");
    expected.addPath("A", "B", "C");
    expected.addPath("B", "D", "E", "F");

    assert.graphEqual(expected, dig.alg.prim(g, edgeLabel(g)));
  });
});
<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import Graph from 'react-graph-vis';
import styles from './Visualization.css'; // eslint-disable-line flowtype-errors/show-errors

export default class Visualization extends Component {
  props: {
    json: Object
  }

  resources: any;

  findImage(resourceType: string) {
    switch (resourceType) {
      case 'Microsoft.Web/serverfarms':
        return '../resources/azure/Azure App Service_COLOR.png';
      case 'Microsoft.Web/sites':
        return '../resources/azure/Azure App Service - Web App_COLOR.png';
      case 'Microsoft.Insights/components':
        return '../resources/azure/Azure Application Insights_COLOR.png';
      case 'Microsoft.Storage/storageAccounts':
        return '../resources/azure/Azure Storage.png';
      case 'Microsoft.EventHub/namespaces':
        return '../resources/azure/Azure Event Hubs_COLOR.png';
      case 'Microsoft.Network/trafficManagerProfiles':
        return '../resources/azure/Azure Traffic Manager_COLOR.png';
      case 'Microsoft.Network/trafficManagerProfiles/azureEndpoints':
        return '../resources/azure/Azure Traffic Manager.png';
      case 'Microsoft.NotificationHubs/namespaces':
        return '../resources/azure/Azure Notification Hubs_COLOR.png';
      case 'Microsoft.Resources/deployments':
        return '../resources/azure/Unidentified feature object_COLOR.png';
      default:
        return '../resources/azure/Unidentified feature object_COLOR.png';
    }
  }

  render() {
    this.resources = this.props.json.resources;

    const resources = [];
    for (let i = 0; i < this.resources.length; i += 1) {
      resources.push({ id: i, label: this.resources[i].name, shape: 'image', image: this.findImage(this.resources[i].type) });
    }

    const graph = {
      nodes: resources,
      edges: []
    };

    const options = {
      nodes: {
        borderWidth: 4,
        size: 30,
        color: {
          border: '#222222',
          background: '#666666'
        },
        font: {
          color: '#fff'
        }
      },
      layout: {
        hierarchical: false
      },
      edges: {
        color: '#FFFFFF'
      },
      autoResize: true
    };

    return (
<Graph graph={graph} options={options} style={{ width: '100%', height: '100%' }} />
);
  }
}
<commit_msg>#2 - eslint fixes<commit_after>// @flow
import React, { Component } from 'react';
import Graph from 'react-graph-vis';
import styles from './Visualization.css'; // eslint-disable-line flowtype-errors/show-errors

export default class Visualization extends Component {
  props: {
    json: Object
  }

  resources: any;

  static findImage(resourceType: string) {
    switch (resourceType) {
      case 'Microsoft.Web/serverfarms':
        return '../resources/azure/Azure App Service_COLOR.png';
      case 'Microsoft.Web/sites':
        return '../resources/azure/Azure App Service - Web App_COLOR.png';
      case 'Microsoft.Insights/components':
        return '../resources/azure/Azure Application Insights_COLOR.png';
      case 'Microsoft.Storage/storageAccounts':
        return '../resources/azure/Azure Storage.png';
      case 'Microsoft.EventHub/namespaces':
        return '../resources/azure/Azure Event Hubs_COLOR.png';
      case 'Microsoft.Network/trafficManagerProfiles':
        return '../resources/azure/Azure Traffic Manager_COLOR.png';
      case 'Microsoft.Network/trafficManagerProfiles/azureEndpoints':
        return '../resources/azure/Azure Traffic Manager.png';
      case 'Microsoft.NotificationHubs/namespaces':
        return '../resources/azure/Azure Notification Hubs_COLOR.png';
      case 'Microsoft.Resources/deployments':
        return '../resources/azure/Unidentified feature object_COLOR.png';
      default:
        return '../resources/azure/Unidentified feature object_COLOR.png';
    }
  }

  render() {
    this.resources = this.props.json.resources;

    const resources = [];
    for (let i = 0; i < this.resources.length; i += 1) {
      resources.push({ id: i, label: this.resources[i].name, shape: 'image', image: this.findImage(this.resources[i].type) });
    }

    const graph = {
      nodes: resources,
      edges: []
    };

    const options = {
      nodes: {
        borderWidth: 4,
        size: 30,
        color: {
          border: '#222222',
          background: '#666666'
        },
        font: {
          color: '#fff'
        }
      },
      layout: {
        hierarchical: false
      },
      edges: {
        color: '#FFFFFF'
      },
      autoResize: true
    };

    return (
<Graph graph={graph} options={options} style={{ width: '100%', height: '100%' }} />
);
  }
}
<|endoftext|>


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

import {LayerGroup, MaskLayer} from 'model';
import {ColorUtil} from 'colorutil';
import {ElementResizeWatcher} from 'elementresizewatcher';

const CANVAS_MARGIN = 72; // pixels

class CanvasController {
  constructor($scope, $element, $attrs, StudioStateService, $timeout) {
    this.scope_ = $scope;
    this.element_ = $element;
    this.attrs_ = $attrs;
    this.canvas_ = $element.find('canvas');
    this.offscreenCanvas_ = $(document.createElement('canvas'));
    this.studioState_ = StudioStateService;
    this.registeredRulers_ = [];

    this.isPreviewMode = 'previewMode' in $attrs;
    if (this.isPreviewMode) {
      this.element_.addClass('preview-mode');
    }

    this.setupMouseEventHandlers_();

    let changeHandler_ = (event, changes) => {
      if (changes.playing) {
        if (this.studioState_.playing) {
          this.animStart = Number(new Date())
              - this.studioState_.activeTime / this.studioState_.playbackSpeed;
        }

        this.drawCanvas_();
      }

      if (changes.activeTime) {
        this.animTime = this.studioState_.activeTime;
        this.drawCanvas_();
      }

      if (changes.selection) {
        this.drawCanvas_();
      }

      if (changes.artwork || changes.animations || changes.activeAnimation) {
        this.resizeAndDrawCanvas_();
      }
    };

    changeHandler_(null, {playing: true, activeTime: true, artwork: true});

    this.studioState_.onChange(changeHandler_, $scope);

    if (!this.isPreviewMode) {
      let resizeWatcher = new ElementResizeWatcher(
          this.element_, () => this.resizeAndDrawCanvas_());
      $scope.$on('$destroy', () => resizeWatcher.destroy());
    }

    $timeout(() => this.resizeAndDrawCanvas_(), 0);
  }

  setupMouseEventHandlers_() {
    this.canvas_
        .on('mousemove', event => {
          let canvasOffset = this.canvas_.offset();
          let x = Math.round((event.pageX - canvasOffset.left) / this.scale_);
          let y = Math.round((event.pageY - canvasOffset.top) / this.scale_);
          this.registeredRulers_.forEach(r => r.showMousePosition(x, y));
        })
        .on('mouseleave', () => {
          this.registeredRulers_.forEach(r => r.hideMouse());
        });
  }

  get artwork() {
    return this.studioState_.artwork;
  }

  get animation() {
    return this.studioState_.activeAnimation;
  }

  registerRuler(rulerScope) {
    this.registeredRulers_.push(rulerScope);
    this.redrawRulers_();
  }

  unregisterRuler(rulerScope) {
    let idx = this.registeredRulers_.indexOf(rulerScope);
    if (idx >= 0) {
      this.registeredRulers_.splice(idx, 1);
    }
  }

  redrawRulers_() {
    this.registeredRulers_.forEach(r => {
      r.setArtworkSize({
        width: this.artwork.width,
        height: this.artwork.height
      });
      r.redraw();
    });
  }

  resizeAndDrawCanvas_() {
    if (this.isPreviewMode) {
      this.scale_ = 1;
    } else {
      let containerWidth = Math.max(1, this.element_.width() - CANVAS_MARGIN * 2);
      let containerHeight = Math.max(1, this.element_.height() - CANVAS_MARGIN * 2);
      let containerAspectRatio = containerWidth / containerHeight;
      let artworkAspectRatio = this.artwork.width / (this.artwork.height || 1);

      if (artworkAspectRatio > containerAspectRatio) {
        this.scale_ = containerWidth / this.artwork.width;
      } else {
        this.scale_ = containerHeight / this.artwork.height;
      }
    }

    this.scale_ = Math.max(1, Math.floor(this.scale_));
    this.backingStoreScale_ = this.scale_ * (window.devicePixelRatio || 1);
    [this.canvas_, this.offscreenCanvas_].forEach(canvas => {
      canvas
          .attr({
            width: this.artwork.width * this.backingStoreScale_,
            height: this.artwork.height * this.backingStoreScale_,
          })
          .css({
            width: this.artwork.width * this.scale_,
            height: this.artwork.height * this.scale_,
          });
    });

    this.drawCanvas_();
    this.redrawRulers_();
  }

  drawCanvas_() {
    if (this.animationFrameRequest_) {
      window.cancelAnimationFrame(this.animationFrameRequest_);
      this.animationFrameRequest_ = null;
    }

    if (!this.artwork) {
      return;
    }

    let ctx = this.canvas_.get(0).getContext('2d');
    ctx.save();
    ctx.scale(this.backingStoreScale_, this.backingStoreScale_);
    ctx.clearRect(0, 0, this.artwork.width, this.artwork.height);
    if (this.artwork.canvasColor) {
      ctx.fillStyle = ColorUtil.androidToCssColor(this.artwork.canvasColor);
      ctx.fillRect(0, 0, this.artwork.width, this.artwork.height);
    }

    let selectionStroke_ = extraSetupFn => {
      ctx.save();
      // ctx.globalCompositeOperation = 'exclusion';
      extraSetupFn && extraSetupFn();
      ctx.lineWidth = 6 / this.scale_; // 2px
      ctx.strokeStyle = '#fff';
      ctx.lineCap = 'round';
      ctx.stroke();
      ctx.strokeStyle = '#2196f3';
      ctx.lineWidth = 3 / this.scale_; // 2px
      ctx.stroke();
      ctx.restore();
    };

    let transforms = [];

    let drawLayer_ = (ctx, layer, selectionMode) => {
      if (layer instanceof LayerGroup) {
        transforms.push(() => {
          ctx.translate(layer.pivotX, layer.pivotY);
          ctx.translate(layer.translateX, layer.translateY);
          ctx.rotate(layer.rotation * Math.PI / 180);
          ctx.scale(layer.scaleX, layer.scaleY);
          ctx.translate(-layer.pivotX, -layer.pivotY);
        });

        ctx.save();
        layer.layers.forEach(layer => drawLayer_(ctx, layer, selectionMode));
        ctx.restore();

        if (selectionMode && layer.selected) {
          let bounds = layer.computeBounds();
          if (bounds) {
            ctx.save();
            transforms.forEach(t => t());
            ctx.beginPath();
            ctx.rect(bounds.l, bounds.t, bounds.r - bounds.l, bounds.b - bounds.t);
            ctx.restore();
            selectionStroke_();
          }
        }

        transforms.pop();
      } else if (layer instanceof MaskLayer) {
        ctx.save();
        transforms.forEach(t => t());
        layer.pathData && layer.pathData.execute(ctx);
        ctx.restore();

        if (!selectionMode) {
          // clip further layers
          ctx.clip();
        } else if (selectionMode && layer.selected) {
          // this layer is selected, draw the layer selection stuff
          selectionStroke_(() => ctx.setLineDash([5 / this.scale_, 5 / this.scale_]));
        }

      } else {
        ctx.save();
        transforms.forEach(t => t());
        layer.pathData && layer.pathData.execute(ctx);
        ctx.restore();

        if (!selectionMode) {
          // draw the actual layer
          ctx.strokeStyle = ColorUtil.androidToCssColor(layer.strokeColor, layer.strokeAlpha);
          ctx.lineWidth = layer.strokeWidth;
          ctx.fillStyle = ColorUtil.androidToCssColor(layer.fillColor, layer.fillAlpha);
          ctx.lineCap = layer.strokeLinecap || 'butt';
          ctx.lineJoin = layer.strokeLinejoin || 'miter';
          ctx.miterLimit = layer.miterLimit || 4;

          if (layer.trimPathStart !== 0
              || layer.trimPathEnd !== 1
              || layer.trimPathOffset !== 0) {
            // Calculate the visible fraction of the trimmed path. If trimPathStart
            // is greater than trimPathEnd, then the result should be the combined
            // length of the two line segments: [trimPathStart,1] and [0,trimPathEnd].
            let shownFraction = layer.trimPathEnd - layer.trimPathStart;
            if (layer.trimPathStart > layer.trimPathEnd) {
              shownFraction += 1;
            }

            // Calculate the dash array. The first array element is the length of
            // the trimmed path and the second element is the gap, which is the
            // difference in length between the total path length and the visible
            // trimmed path length.
            ctx.setLineDash([
              shownFraction * layer.pathData.length,
              (1 - shownFraction + 0.001) * layer.pathData.length
            ]);

            // The amount to offset the path is equal to the trimPathStart plus
            // trimPathOffset. We mod the result because the trimmed path
            // should wrap around once it reaches 1.
            ctx.lineDashOffset = layer.pathData.length
                * (1 - ((layer.trimPathStart + layer.trimPathOffset) % 1));
          } else {
            ctx.setLineDash([]);
          }

          if (layer.strokeColor
              && layer.strokeWidth
              && layer.trimPathStart != layer.trimPathEnd) {
            ctx.stroke();
          }
          if (layer.fillColor) {
            ctx.fill();
          }
        } else if (selectionMode && layer.selected) {
          // this layer is selected, draw the layer selection stuff
          selectionStroke_();
        }
      }
    };

    // draw artwork
    let offscreenCtx = this.offscreenCanvas_.get(0).getContext('2d');
    let currentArtwork;
    if (this.studioState_.animationRenderer) {
      this.studioState_.animationRenderer.setAnimationTime(this.animTime || 0);
      currentArtwork = this.studioState_.animationRenderer.renderedArtwork;
    } else {
      currentArtwork = this.artwork;
    }
    let currentAlpha = currentArtwork.alpha;
    if (currentAlpha != 1) {
      offscreenCtx.save();
      offscreenCtx.scale(this.backingStoreScale_, this.backingStoreScale_);
      offscreenCtx.clearRect(0, 0, currentArtwork.width, currentArtwork.height);
    }
    let artworkCtx = currentAlpha == 1 ? ctx : offscreenCtx;
    drawLayer_(artworkCtx, currentArtwork);
    if (!this.isPreviewMode) {
      drawLayer_(artworkCtx, currentArtwork, true);
    }

    if (currentAlpha != 1) {
      let oldGlobalAlpha = ctx.globalAlpha;
      ctx.globalAlpha = currentAlpha;
      ctx.scale(1 / this.backingStoreScale_, 1 / this.backingStoreScale_);
      ctx.drawImage(offscreenCtx.canvas, 0, 0);
      ctx.scale(this.backingStoreScale_, this.backingStoreScale_);
      ctx.globalAlpha = oldGlobalAlpha;
      offscreenCtx.restore();
    }

    ctx.restore();

    // draw pixel grid
    if (!this.isPreviewMode && this.scale_ > 4) {
      ctx.fillStyle = 'rgba(128, 128, 128, .25)';

      for (let x = 1; x < this.artwork.width; ++x) {
        ctx.fillRect(
            x * this.backingStoreScale_ - 0.5 * (window.devicePixelRatio || 1),
            0,
            1 * (window.devicePixelRatio || 1),
            this.artwork.height * this.backingStoreScale_);
      }

      for (let y = 1; y < this.artwork.height; ++y) {
        ctx.fillRect(
            0,
            y * this.backingStoreScale_ - 0.5 * (window.devicePixelRatio || 1),
            this.artwork.width * this.backingStoreScale_,
            1 * (window.devicePixelRatio || 1));
      }
    }

    if (this.studioState_.playing) {
      this.animationFrameRequest_ = window.requestAnimationFrame(() => {
        this.animTime = ((Number(new Date()) - this.animStart) * this.studioState_.playbackSpeed)
            % this.animation.duration;
        this.scope_.$apply(() => this.studioState_.activeTime = this.animTime);
        this.drawCanvas_();
      });
    }
  }
}

angular.module('AVDStudio').directive('studioCanvas', () => {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'components/canvas/canvas.html',
    replace: true,
    bindToController: true,
    controller: CanvasController,
    controllerAs: 'ctrl'
  };
});
<commit_msg>Update canvas.js<commit_after>/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LayerGroup, MaskLayer} from 'model';
import {ColorUtil} from 'colorutil';
import {ElementResizeWatcher} from 'elementresizewatcher';

const CANVAS_MARGIN = 72; // pixels

class CanvasController {
  constructor($scope, $element, $attrs, StudioStateService, $timeout) {
    this.scope_ = $scope;
    this.element_ = $element;
    this.attrs_ = $attrs;
    this.canvas_ = $element.find('canvas');
    this.offscreenCanvas_ = $(document.createElement('canvas'));
    this.studioState_ = StudioStateService;
    this.registeredRulers_ = [];

    this.isPreviewMode = 'previewMode' in $attrs;
    if (this.isPreviewMode) {
      this.element_.addClass('preview-mode');
    }

    this.setupMouseEventHandlers_();

    let changeHandler_ = (event, changes) => {
      if (changes.playing) {
        if (this.studioState_.playing) {
          this.animStart = Number(new Date())
              - this.studioState_.activeTime / this.studioState_.playbackSpeed;
        }

        this.drawCanvas_();
      }

      if (changes.activeTime) {
        this.animTime = this.studioState_.activeTime;
        this.drawCanvas_();
      }

      if (changes.selection) {
        this.drawCanvas_();
      }

      if (changes.artwork || changes.animations || changes.activeAnimation) {
        this.resizeAndDrawCanvas_();
      }
    };

    changeHandler_(null, {playing: true, activeTime: true, artwork: true});

    this.studioState_.onChange(changeHandler_, $scope);

    if (!this.isPreviewMode) {
      let resizeWatcher = new ElementResizeWatcher(
          this.element_, () => this.resizeAndDrawCanvas_());
      $scope.$on('$destroy', () => resizeWatcher.destroy());
    }

    $timeout(() => this.resizeAndDrawCanvas_(), 0);
  }

  setupMouseEventHandlers_() {
    this.canvas_
        .on('mousemove', event => {
          let canvasOffset = this.canvas_.offset();
          let x = Math.round((event.pageX - canvasOffset.left) / this.scale_);
          let y = Math.round((event.pageY - canvasOffset.top) / this.scale_);
          this.registeredRulers_.forEach(r => r.showMousePosition(x, y));
        })
        .on('mouseleave', () => {
          this.registeredRulers_.forEach(r => r.hideMouse());
        });
  }

  get artwork() {
    return this.studioState_.artwork;
  }

  get animation() {
    return this.studioState_.activeAnimation;
  }

  registerRuler(rulerScope) {
    this.registeredRulers_.push(rulerScope);
    this.redrawRulers_();
  }

  unregisterRuler(rulerScope) {
    let idx = this.registeredRulers_.indexOf(rulerScope);
    if (idx >= 0) {
      this.registeredRulers_.splice(idx, 1);
    }
  }

  redrawRulers_() {
    this.registeredRulers_.forEach(r => {
      r.setArtworkSize({
        width: this.artwork.width,
        height: this.artwork.height
      });
      r.redraw();
    });
  }

  resizeAndDrawCanvas_() {
    if (this.isPreviewMode) {
      this.scale_ = 1;
    } else {
      let containerWidth = Math.max(1, this.element_.width() - CANVAS_MARGIN * 2);
      let containerHeight = Math.max(1, this.element_.height() - CANVAS_MARGIN * 2);
      let containerAspectRatio = containerWidth / containerHeight;
      let artworkAspectRatio = this.artwork.width / (this.artwork.height || 1);

      if (artworkAspectRatio > containerAspectRatio) {
        this.scale_ = containerWidth / this.artwork.width;
      } else {
        this.scale_ = containerHeight / this.artwork.height;
      }
    }

    this.scale_ = Math.max(1, Math.floor(this.scale_));
    this.backingStoreScale_ = this.scale_ * (window.devicePixelRatio || 1);
    [this.canvas_, this.offscreenCanvas_].forEach(canvas => {
      canvas
          .attr({
            width: this.artwork.width * this.backingStoreScale_,
            height: this.artwork.height * this.backingStoreScale_,
          })
          .css({
            width: this.artwork.width * this.scale_,
            height: this.artwork.height * this.scale_,
          });
    });

    this.drawCanvas_();
    this.redrawRulers_();
  }

  drawCanvas_() {
    if (this.animationFrameRequest_) {
      window.cancelAnimationFrame(this.animationFrameRequest_);
      this.animationFrameRequest_ = null;
    }

    if (!this.artwork) {
      return;
    }

    let ctx = this.canvas_.get(0).getContext('2d');
    ctx.save();
    ctx.scale(this.backingStoreScale_, this.backingStoreScale_);
    ctx.clearRect(0, 0, this.artwork.width, this.artwork.height);
    if (this.artwork.canvasColor) {
      ctx.fillStyle = ColorUtil.androidToCssColor(this.artwork.canvasColor);
      ctx.fillRect(0, 0, this.artwork.width, this.artwork.height);
    }

    let selectionStroke_ = extraSetupFn => {
      ctx.save();
      // ctx.globalCompositeOperation = 'exclusion';
      extraSetupFn && extraSetupFn();
      ctx.lineWidth = 6 / this.scale_; // 2px
      ctx.strokeStyle = '#fff';
      ctx.lineCap = 'round';
      ctx.stroke();
      ctx.strokeStyle = '#2196f3';
      ctx.lineWidth = 3 / this.scale_; // 2px
      ctx.stroke();
      ctx.restore();
    };

    let transforms = [];

    let drawLayer_ = (ctx, layer, selectionMode) => {
      if (layer instanceof LayerGroup) {
        transforms.push(() => {
          ctx.translate(layer.pivotX, layer.pivotY);
          ctx.translate(layer.translateX, layer.translateY);
          ctx.rotate(layer.rotation * Math.PI / 180);
          ctx.scale(layer.scaleX, layer.scaleY);
          ctx.translate(-layer.pivotX, -layer.pivotY);
        });

        ctx.save();
        layer.layers.forEach(layer => drawLayer_(ctx, layer, selectionMode));
        ctx.restore();

        if (selectionMode && layer.selected) {
          let bounds = layer.computeBounds();
          if (bounds) {
            ctx.save();
            transforms.forEach(t => t());
            ctx.beginPath();
            ctx.rect(bounds.l, bounds.t, bounds.r - bounds.l, bounds.b - bounds.t);
            ctx.restore();
            selectionStroke_();
          }
        }

        transforms.pop();
      } else if (layer instanceof MaskLayer) {
        ctx.save();
        transforms.forEach(t => t());
        layer.pathData && layer.pathData.execute(ctx);
        ctx.restore();

        if (!selectionMode) {
          // clip further layers
          ctx.clip();
        } else if (selectionMode && layer.selected) {
          // this layer is selected, draw the layer selection stuff
          selectionStroke_(() => ctx.setLineDash([5 / this.scale_, 5 / this.scale_]));
        }

      } else {
        ctx.save();
        transforms.forEach(t => t());
        layer.pathData && layer.pathData.execute(ctx);
        ctx.restore();

        if (!selectionMode) {
          // draw the actual layer
          ctx.strokeStyle = ColorUtil.androidToCssColor(layer.strokeColor, layer.strokeAlpha);
          ctx.lineWidth = layer.strokeWidth;
          ctx.fillStyle = ColorUtil.androidToCssColor(layer.fillColor, layer.fillAlpha);
          ctx.lineCap = layer.strokeLinecap || 'butt';
          ctx.lineJoin = layer.strokeLinejoin || 'miter';
          ctx.miterLimit = layer.miterLimit || 4;

          if (layer.trimPathStart !== 0
              || layer.trimPathEnd !== 1
              || layer.trimPathOffset !== 0) {
            // Calculate the visible fraction of the trimmed path. If trimPathStart
            // is greater than trimPathEnd, then the result should be the combined
            // length of the two line segments: [trimPathStart,1] and [0,trimPathEnd].
            let shownFraction = layer.trimPathEnd - layer.trimPathStart;
            if (layer.trimPathStart > layer.trimPathEnd) {
              shownFraction += 1;
            }

            // Calculate the dash array. The first array element is the length of
            // the trimmed path and the second element is the gap, which is the
            // difference in length between the total path length and the visible
            // trimmed path length.
            ctx.setLineDash([
              shownFraction * layer.pathData.length,
              (1 - shownFraction + 0.001) * layer.pathData.length
            ]);

            // The amount to offset the path is equal to the trimPathStart plus
            // trimPathOffset. We mod the result because the trimmed path
            // should wrap around once it reaches 1.
            ctx.lineDashOffset = layer.pathData.length
                * (1 - ((layer.trimPathStart + layer.trimPathOffset) % 1));
          } else {
            ctx.setLineDash([]);
          }

          if (layer.strokeColor
              && layer.strokeWidth
              && layer.trimPathStart != layer.trimPathEnd) {
            ctx.stroke();
          }
          if (layer.fillColor) {
            ctx.fill();
          }
        } else if (selectionMode && layer.selected) {
          // this layer is selected, draw the layer selection stuff
          selectionStroke_();
        }
      }
    };

    // draw artwork
    let offscreenCtx = this.offscreenCanvas_.get(0).getContext('2d');
    let currentArtwork;
    if (this.studioState_.animationRenderer) {
      this.studioState_.animationRenderer.setAnimationTime(this.animTime || 0);
      currentArtwork = this.studioState_.animationRenderer.renderedArtwork;
    } else {
      currentArtwork = this.artwork;
    }
    let currentAlpha = currentArtwork.alpha;
    if (currentAlpha != 1) {
      offscreenCtx.save();
      offscreenCtx.scale(this.backingStoreScale_, this.backingStoreScale_);
      offscreenCtx.clearRect(0, 0, currentArtwork.width, currentArtwork.height);
    }
    let artworkCtx = currentAlpha == 1 ? ctx : offscreenCtx;
    drawLayer_(artworkCtx, currentArtwork);
    if (!this.isPreviewMode) {
      drawLayer_(artworkCtx, currentArtwork, true);
    }

    if (currentArtwork.alpha != 1) {
      let oldGlobalAlpha = ctx.globalAlpha;
      ctx.globalAlpha = currentAlpha;
      ctx.scale(1 / this.backingStoreScale_, 1 / this.backingStoreScale_);
      ctx.drawImage(offscreenCtx.canvas, 0, 0);
      ctx.scale(this.backingStoreScale_, this.backingStoreScale_);
      ctx.globalAlpha = oldGlobalAlpha;
      offscreenCtx.restore();
    }

    ctx.restore();

    // draw pixel grid
    if (!this.isPreviewMode && this.scale_ > 4) {
      ctx.fillStyle = 'rgba(128, 128, 128, .25)';

      for (let x = 1; x < this.artwork.width; ++x) {
        ctx.fillRect(
            x * this.backingStoreScale_ - 0.5 * (window.devicePixelRatio || 1),
            0,
            1 * (window.devicePixelRatio || 1),
            this.artwork.height * this.backingStoreScale_);
      }

      for (let y = 1; y < this.artwork.height; ++y) {
        ctx.fillRect(
            0,
            y * this.backingStoreScale_ - 0.5 * (window.devicePixelRatio || 1),
            this.artwork.width * this.backingStoreScale_,
            1 * (window.devicePixelRatio || 1));
      }
    }

    if (this.studioState_.playing) {
      this.animationFrameRequest_ = window.requestAnimationFrame(() => {
        this.animTime = ((Number(new Date()) - this.animStart) * this.studioState_.playbackSpeed)
            % this.animation.duration;
        this.scope_.$apply(() => this.studioState_.activeTime = this.animTime);
        this.drawCanvas_();
      });
    }
  }
}

angular.module('AVDStudio').directive('studioCanvas', () => {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'components/canvas/canvas.html',
    replace: true,
    bindToController: true,
    controller: CanvasController,
    controllerAs: 'ctrl'
  };
});
<|endoftext|>