| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 | import Cards from '/models/cards';import Boards from '/models/boards';const subManager = new SubsManager();const { calculateIndexData, capitalize } = Utils;function initSorting(items) {  items.sortable({    tolerance: 'pointer',    helper: 'clone',    items: '.js-checklist-item:not(.placeholder)',    connectWith: '.js-checklist-items',    appendTo: 'parent',    distance: 7,    placeholder: 'checklist-item placeholder',    scroll: true,    start(evt, ui) {      ui.placeholder.height(ui.helper.height());      EscapeActions.clickExecute(evt.target, 'inlinedForm');    },    stop(evt, ui) {      const parent = ui.item.parents('.js-checklist-items');      const checklistId = Blaze.getData(parent.get(0)).checklist._id;      let prevItem = ui.item.prev('.js-checklist-item').get(0);      if (prevItem) {        prevItem = Blaze.getData(prevItem).item;      }      let nextItem = ui.item.next('.js-checklist-item').get(0);      if (nextItem) {        nextItem = Blaze.getData(nextItem).item;      }      const nItems = 1;      const sortIndex = calculateIndexData(prevItem, nextItem, nItems);      const checklistDomElement = ui.item.get(0);      const checklistData = Blaze.getData(checklistDomElement);      const checklistItem = checklistData.item;      items.sortable('cancel');      checklistItem.move(checklistId, sortIndex.base);    },  });}BlazeComponent.extendComponent({  onRendered() {    const self = this;    self.itemsDom = this.$('.js-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('uiSortable') || $itemsDom.data('sortable')) {        $(self.itemsDom).sortable('option', 'disabled', !userIsMember());        if (Utils.isMiniScreenOrShowDesktopDragHandles()) {          $(self.itemsDom).sortable({            handle: 'span.fa.checklistitem-handle',          });        }      }    });  },  canModifyCard() {    return (      Meteor.user() &&      Meteor.user().isBoardMember() &&      !Meteor.user().isCommentOnly() &&      !Meteor.user().isWorker()    );  },  /** returns the finished percent of the checklist */  finishedPercent() {    const ret = this.data().checklist.finishedPercent();    return ret;  },}).register('checklistDetail');BlazeComponent.extendComponent({  addChecklist(event) {    event.preventDefault();    const textarea = this.find('textarea.js-add-checklist-item');    const title = textarea.value.trim();    let cardId = this.currentData().cardId;    const card = Cards.findOne(cardId);    //if (card.isLinked()) cardId = card.linkedId;    if (card.isLinkedCard()) cardId = card.linkedId;    if (title) {      Checklists.insert({        cardId,        title,        sort: card.checklists().count(),      });      this.closeAllInlinedForms();      setTimeout(() => {        this.$('.add-checklist-item')          .last()          .click();      }, 100);    }  },  addChecklistItem(event) {    event.preventDefault();    const textarea = this.find('textarea.js-add-checklist-item');    const newlineBecomesNewChecklistItem = this.find('input#toggleNewlineBecomesNewChecklistItem');    const title = textarea.value.trim();    const checklist = this.currentData().checklist;    if (title) {      let checklistItems = [title];      if (newlineBecomesNewChecklistItem.checked) {        checklistItems = title.split('\n').map(_value => _value.trim());      }      for (let checklistItem of checklistItems) {        ChecklistItems.insert({          title: checklistItem,          checklistId: checklist._id,          cardId: checklist.cardId,          sort: Utils.calculateIndexData(checklist.lastItem()).base,        });      }    }    // We keep the form opened, empty it.    textarea.value = '';    textarea.focus();  },  canModifyCard() {    return (      Meteor.user() &&      Meteor.user().isBoardMember() &&      !Meteor.user().isCommentOnly() &&      !Meteor.user().isWorker()    );  },  deleteItem() {    const checklist = this.currentData().checklist;    const item = this.currentData().item;    if (checklist && item && item._id) {      ChecklistItems.remove(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 item = this.currentData().item;    item.setTitle(title);  },  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();    }  },  focusChecklistItem(event) {    // If a new checklist is created, pre-fill the title and select it.    const checklist = this.currentData().checklist;    if (!checklist) {      const textarea = event.target;      textarea.value = capitalize(TAPi18n.__('r-checklist'));      textarea.select();    }  },  /** closes all inlined forms (checklist and checklist-item input fields) */  closeAllInlinedForms() {    this.$('.js-close-inlined-form').click();  },  events() {    const events = {      'click #toggleHideCheckedItemsButton'() {        Meteor.call('toggleHideCheckedItems');      },    };    return [      {        ...events,        'click .js-open-checklist-details-menu': Popup.open('checklistActions'),        '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-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),        'click .js-delete-checklist-item': this.deleteItem,        'focus .js-add-checklist-item': this.focusChecklistItem,        // add and delete checklist / checklist-item        'click .js-open-inlined-form': this.closeAllInlinedForms,        keydown: this.pressKey,      },    ];  },}).register('checklists');BlazeComponent.extendComponent({  onCreated() {    subManager.subscribe('board', Session.get('currentBoard'), false);    this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));  },  boards() {    return Boards.find(      {        archived: false,        'members.userId': Meteor.userId(),        _id: { $ne: Meteor.user().getTemplatesBoardId() },      },      {        sort: { sort: 1 /* boards default sorting */ },      },    );  },  swimlanes() {    const board = Boards.findOne(this.selectedBoardId.get());    return board.swimlanes();  },  aBoardLists() {    const board = Boards.findOne(this.selectedBoardId.get());    return board.lists();  },  events() {    return [      {        'change .js-select-boards'(event) {          this.selectedBoardId.set($(event.currentTarget).val());          subManager.subscribe('board', this.selectedBoardId.get(), false);        },      },    ];  },}).register('boardsSwimlanesAndLists');Template.checklists.helpers({  checklists() {    const card = Cards.findOne(this.cardId);    const ret = card.checklists();    return ret;  },  hideCheckedItems() {    const currentUser = Meteor.user();    if (currentUser) return currentUser.hasHideCheckedItems();    return false;  },});BlazeComponent.extendComponent({  onRendered() {    autosize(this.$('textarea.js-add-checklist-item'));  },  canModifyCard() {    return (      Meteor.user() &&      Meteor.user().isBoardMember() &&      !Meteor.user().isCommentOnly() &&      !Meteor.user().isWorker()    );  },  events() {    return [      {        'click a.fa.fa-copy'(event) {          const $editor = this.$('textarea');          const promise = Utils.copyTextToClipboard($editor[0].value);          const $tooltip = this.$('.copied-tooltip');          Utils.showCopied(promise, $tooltip);        },      }    ];  }}).register('addChecklistItemForm');BlazeComponent.extendComponent({  events() {    return [      {        'click .js-delete-checklist' : Popup.afterConfirm('checklistDelete', function () {          Popup.back(2);          const checklist = this.checklist;          if (checklist && checklist._id) {            Checklists.remove(checklist._id);          }        }),        'click .js-move-checklist' : Popup.open('moveChecklist'),        'click .js-copy-checklist' : Popup.open('copyChecklist'),      }    ]  }}).register('checklistActionsPopup');BlazeComponent.extendComponent({  onRendered() {    autosize(this.$('textarea.js-edit-checklist-item'));  },  canModifyCard() {    return (      Meteor.user() &&      Meteor.user().isBoardMember() &&      !Meteor.user().isCommentOnly() &&      !Meteor.user().isWorker()    );  },  events() {    return [      {        'click a.fa.fa-copy'(event) {          const $editor = this.$('textarea');          const promise = Utils.copyTextToClipboard($editor[0].value);          const $tooltip = this.$('.copied-tooltip');          Utils.showCopied(promise, $tooltip);        },      }    ];  }}).register('editChecklistItemForm');Template.checklistItemDetail.helpers({  canModifyCard() {    return (      Meteor.user() &&      Meteor.user().isBoardMember() &&      !Meteor.user().isCommentOnly() &&      !Meteor.user().isWorker()    );  },  hideCheckedItems() {    const user = Meteor.user();    if (user) return user.hasHideCheckedItems();    return false;  },});BlazeComponent.extendComponent({  toggleItem() {    const checklist = this.currentData().checklist;    const item = this.currentData().item;    if (checklist && item && item._id) {      item.toggleItem();    }  },  events() {    return [      {        'click .js-checklist-item .check-box-container': this.toggleItem,      },    ];  },}).register('checklistItemDetail');class DialogWithBoardSwimlaneListAndCard extends BlazeComponent {  /** returns the checklist dialog options   * @return Object with properties { boardId, swimlaneId, listId, cardId }   */  getChecklistDialogOptions() {  }  /** checklist is done   * @param cardId the selected card id   * @param options the selected options (Object with properties { boardId, swimlaneId, listId, cardId })   */  setDone(cardId, options) {  }  onCreated() {    this.currentBoardId = Utils.getCurrentBoardId();    this.selectedBoardId = new ReactiveVar(this.currentBoardId);    this.selectedSwimlaneId = new ReactiveVar('');    this.selectedListId = new ReactiveVar('');    this.setChecklistDialogOption(this.currentBoardId);  }  /** set the last confirmed dialog field values   * @param boardId the current board id   */  setChecklistDialogOption(boardId) {    this.checklistDialogOption = {      'boardId' : "",      'swimlaneId' : "",      'listId' : "",      'cardId': "",    }    let currentOptions = this.getChecklistDialogOptions();    if (currentOptions && boardId && currentOptions[boardId]) {      this.checklistDialogOption = currentOptions[boardId];      if (this.checklistDialogOption.boardId &&          this.checklistDialogOption.swimlaneId &&          this.checklistDialogOption.listId      )      {        this.selectedBoardId.set(this.checklistDialogOption.boardId)        this.selectedSwimlaneId.set(this.checklistDialogOption.swimlaneId);        this.selectedListId.set(this.checklistDialogOption.listId);      }    }    this.getBoardData(this.selectedBoardId.get());    if (!this.selectedSwimlaneId.get() || !Swimlanes.findOne({_id: this.selectedSwimlaneId.get(), boardId: this.selectedBoardId.get()})) {      this.setFirstSwimlaneId();    }    if (!this.selectedListId.get() || !Lists.findOne({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()})) {      this.setFirstListId();    }  }  /** sets the first swimlane id */  setFirstSwimlaneId() {    try {      const board = Boards.findOne(this.selectedBoardId.get());      const swimlaneId = board.swimlanes().fetch()[0]._id;      this.selectedSwimlaneId.set(swimlaneId);    } catch (e) {}  }  /** sets the first list id */  setFirstListId() {    try {      const board = Boards.findOne(this.selectedBoardId.get());      const listId = board.lists().fetch()[0]._id;      this.selectedListId.set(listId);    } catch (e) {}  }  /** returns if the board id was the last confirmed one   * @param boardId check this board id   * @return if the board id was the last confirmed one   */  isChecklistDialogOptionBoardId(boardId) {    let ret = this.checklistDialogOption.boardId == boardId;    return ret;  }  /** returns if the swimlane id was the last confirmed one   * @param swimlaneId check this swimlane id   * @return if the swimlane id was the last confirmed one   */  isChecklistDialogOptionSwimlaneId(swimlaneId) {    let ret = this.checklistDialogOption.swimlaneId == swimlaneId;    return ret;  }  /** returns if the list id was the last confirmed one   * @param listId check this list id   * @return if the list id was the last confirmed one   */  isChecklistDialogOptionListId(listId) {    let ret = this.checklistDialogOption.listId == listId;    return ret;  }  /** returns if the card id was the last confirmed one   * @param cardId check this card id   * @return if the card id was the last confirmed one   */  isChecklistDialogOptionCardId(cardId) {    let ret = this.checklistDialogOption.cardId == cardId;    return ret;  }  /** returns all available board */  boards() {    const ret = Boards.find(      {        archived: false,        'members.userId': Meteor.userId(),        _id: { $ne: Meteor.user().getTemplatesBoardId() },      },      {        sort: { sort: 1 },      },    );    return ret;  }  /** returns all available swimlanes of the current board */  swimlanes() {    const board = Boards.findOne(this.selectedBoardId.get());    const ret = board.swimlanes();    return ret;  }  /** returns all available lists of the current board */  lists() {    const board = Boards.findOne(this.selectedBoardId.get());    const ret = board.lists();    return ret;  }  /** returns all available cards of the current list */  cards() {    const list = Lists.findOne(this.selectedListId.get());    const ret = list.cards(this.selectedSwimlaneId.get());    return ret;  }  /** get the board data from the server   * @param boardId get the board data of this board id   */  getBoardData(boardId) {    const self = this;    Meteor.subscribe('board', boardId, false, {      onReady() {        const sameBoardId = self.selectedBoardId.get() == boardId;        self.selectedBoardId.set(boardId);        if (!sameBoardId) {          // reset swimlane id (for selection in cards())          self.setFirstSwimlaneId();          // reset list id (for selection in cards())          self.setFirstListId();        }      },    });  }  events() {    return [      {        'click .js-done'() {          const boardSelect = this.$('.js-select-boards')[0];          const boardId = boardSelect.options[boardSelect.selectedIndex].value;          const listSelect = this.$('.js-select-lists')[0];          const listId = listSelect.options[listSelect.selectedIndex].value;          const swimlaneSelect = this.$('.js-select-swimlanes')[0];          const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;          const cardSelect = this.$('.js-select-cards')[0];          const cardId = cardSelect.options[cardSelect.selectedIndex].value;          const options = {            'boardId' : boardId,            'swimlaneId' : swimlaneId,            'listId' : listId,            'cardId': cardId,          }          this.setDone(cardId, options);          Popup.back(2);        },        'change .js-select-boards'(event) {          const boardId = $(event.currentTarget).val();          this.getBoardData(boardId);        },        'change .js-select-swimlanes'(event) {          this.selectedSwimlaneId.set($(event.currentTarget).val());        },        'change .js-select-lists'(event) {          this.selectedListId.set($(event.currentTarget).val());        },      },    ];  }}/** Move Checklist Dialog */(class extends DialogWithBoardSwimlaneListAndCard {  getChecklistDialogOptions() {    const ret = Meteor.user().getMoveChecklistDialogOptions();    return ret;  }  setDone(cardId, options) {    Meteor.user().setMoveChecklistDialogOption(this.currentBoardId, options);    this.data().checklist.move(cardId);  }}).register('moveChecklistPopup');/** Copy Checklist Dialog */(class extends DialogWithBoardSwimlaneListAndCard {  getChecklistDialogOptions() {    const ret = Meteor.user().getCopyChecklistDialogOptions();    return ret;  }  setDone(cardId, options) {    Meteor.user().setCopyChecklistDialogOption(this.currentBoardId, options);    this.data().checklist.copy(cardId);  }}).register('copyChecklistPopup');
 |