| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 | function initSorting(items) {  items.sortable({    tolerance: 'pointer',    helper: 'clone',    items: '.js-checklist-item:not(.placeholder)',    axis: 'y',    distance: 7,    placeholder: 'placeholder',    scroll: false,    start(evt, ui) {      ui.placeholder.height(ui.helper.height());      EscapeActions.executeUpTo('popup-close');    },    stop(evt, ui) {      const parent = ui.item.parents('.js-checklist-items');      const orderedItems = [];      parent.find('.js-checklist-item').each(function(i, item) {        const checklistItem = Blaze.getData(item).item;        orderedItems.push(checklistItem._id);      });      items.sortable('cancel');      const formerParent = ui.item.parents('.js-checklist-items');      const checklist = Blaze.getData(parent.get(0)).checklist;      const oldChecklist = Blaze.getData(formerParent.get(0)).checklist;      if (oldChecklist._id !== checklist._id) {        const currentItem = Blaze.getData(ui.item.get(0)).item;        for (let i = 0; i < orderedItems.length; i++) {          const itemId = orderedItems[i];          if (itemId !== currentItem._id) continue;          const newItem = {            _id: checklist.getNewItemId(),            title: currentItem.title,            sort: i,            isFinished: currentItem.isFinished,          };          checklist.addFullItem(newItem);          orderedItems[i] = currentItem._id;          oldChecklist.removeItem(itemId);        }      } else {        checklist.sortItems(orderedItems);      }    },  });}Template.checklists.onRendered(function () {  const self = BlazeComponent.getComponentForElement(this.firstNode);  self.itemsDom = this.$('.card-checklist-items');  initSorting(self.itemsDom);  self.itemsDom.mousedown(function(evt) {    evt.stopPropagation();  });  function userIsMember() {    return Meteor.user() && Meteor.user().isBoardMember();  }  // Disable sorting if the current user is not a board member  self.autorun(() => {    const $itemsDom = $(self.itemsDom);    if ($itemsDom.data('sortable')) {      $(self.itemsDom).sortable('option', 'disabled', !userIsMember());    }  });});BlazeComponent.extendComponent({  addChecklist(event) {    event.preventDefault();    const textarea = this.find('textarea.js-add-checklist-item');    const title = textarea.value.trim();    const cardId = this.currentData().cardId;    const card = Cards.findOne(cardId);    if (title) {      Checklists.insert({        cardId,        title,        sort: card.checklists().count(),      });      setTimeout(() => {        this.$('.add-checklist-item').last().click();      }, 100);    }    textarea.value = '';    textarea.focus();  },  addChecklistItem(event) {    event.preventDefault();    const textarea = this.find('textarea.js-add-checklist-item');    const title = textarea.value.trim();    const checklist = this.currentData().checklist;    if (title) {      checklist.addItem(title);    }    // We keep the form opened, empty it.    textarea.value = '';    textarea.focus();  },  canModifyCard() {    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();  },  deleteChecklist() {    const checklist = this.currentData().checklist;    if (checklist && checklist._id) {      Checklists.remove(checklist._id);      this.toggleDeleteDialog.set(false);    }  },  deleteItem() {    const checklist = this.currentData().checklist;    const item = this.currentData().item;    if (checklist && item && item._id) {      checklist.removeItem(item._id);    }  },  editChecklist(event) {    event.preventDefault();    const textarea = this.find('textarea.js-edit-checklist-item');    const title = textarea.value.trim();    const checklist = this.currentData().checklist;    checklist.setTitle(title);  },  editChecklistItem(event) {    event.preventDefault();    const textarea = this.find('textarea.js-edit-checklist-item');    const title = textarea.value.trim();    const itemId = this.currentData().item._id;    const checklist = this.currentData().checklist;    checklist.editItem(itemId, title);  },  onCreated() {    this.toggleDeleteDialog = new ReactiveVar(false);    this.checklistToDelete = null; //Store data context to pass to checklistDeleteDialog template  },  pressKey(event) {    //If user press enter key inside a form, submit it    //Unless the user is also holding down the 'shift' key    if (event.keyCode === 13 && !event.shiftKey) {      event.preventDefault();      const $form = $(event.currentTarget).closest('form');      $form.find('button[type=submit]').click();    }  },  events() {    const events = {      'click .toggle-delete-checklist-dialog'(event) {        if($(event.target).hasClass('js-delete-checklist')){          this.checklistToDelete = this.currentData().checklist; //Store data context        }        this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());      },    };    return [{      ...events,      'submit .js-add-checklist': this.addChecklist,      'submit .js-edit-checklist-title': this.editChecklist,      'submit .js-add-checklist-item': this.addChecklistItem,      'submit .js-edit-checklist-item': this.editChecklistItem,      'click .js-delete-checklist-item': this.deleteItem,      'click .confirm-checklist-delete': this.deleteChecklist,      keydown: this.pressKey,    }];  },}).register('checklists');Template.checklistDeleteDialog.onCreated(() => {  const $cardDetails = this.$('.card-details');  this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position    top: false, //required for smooth scroll animation  };  //Callback's purpose is to only prevent scrolling after animation is complete  $cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });  //Prevent scrolling while dialog is open  $cardDetails.on('scroll', () => {    if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll      $cardDetails.scrollTop(0);    }  });});Template.checklistDeleteDialog.onDestroyed(() => {  const $cardDetails = this.$('.card-details');  $cardDetails.off('scroll'); //Reactivate scrolling  $cardDetails.animate( { scrollTop: this.scrollState.position });});Template.itemDetail.helpers({  canModifyCard() {    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();  },});BlazeComponent.extendComponent({  toggleItem() {    const checklist = this.currentData().checklist;    const item = this.currentData().item;    if (checklist && item && item._id) {      checklist.toggleItem(item._id);    }  },  events() {    return [{      'click .item .check-box': this.toggleItem,    }];  },}).register('itemDetail');
 |