Source: trellinator-libs/Checklist.js

/**
* @class Checklist
* @param data (Object} key/value pairs of 
* information, must at least contain "id",
* can basically just pass in response from Trello API
* @memberof module:TrelloEntities
* @constructor
* @classdesc The Checklist class represents
* a Checklist in Trello. You will typically interact
* with the Checklist object as the return value
* of methods on the Card object.
*
* @example
* card.checklist("Something").items().each(function(item)
* {
*     card.postComment(item.name()+" is "+item.state());
* });
* @example
* card.addChecklist("New List",function(cl)
* {
*     cl.addItem("New Item");
* });
*/
var Checklist = function(data)
{
    this.data            = data;
    this.item_list       = null;
    this.containing_card = null;

    /**
    * Return the id of this checklist
    * @memberof module:TrelloEntities.Checklist
    * @example
    * card.checklist("Something").id();
    */
    this.id = function()
    {
        return this.data.id;
    }
  
    /**
    * Set the name of this checklist
    * @memberof module:TrelloEntities.Checklist
    * @param name {string} the new name for the checklist
    * @example
    * card.checklist("Old Name").setName("New Name");
    */
    this.setName = function(name)
    {
      TrelloApi.put("checklists/"+this.data.id+"/name?value="+encodeURIComponent(name));
      this.data.name = name;
      return this;
    }

    /**
    * Set the position of this checklist
    * @memberof module:TrelloEntities.Checklist
    * @param position {string|number} either top, bottom or a positive number
    * @example
    * card.checklist("My Checklist").setPosition("top");
    */
    this.setPosition = function(position)
    {
      TrelloApi.put("checklists/"+this.data.id+"?pos="+encodeURIComponent(position));
      this.data.pos = position;
      return this;
    }

    /**
    * Delete all items matching the given state
    * @memberof module:TrelloEntities.Checklist
    * @param state {string} either complete or incomplete
    * @example
    * card.checklist("Something").deleteItems("complete");
    */
    this.deleteItems = function(state)
    {
        this.items().each(function(elem)
        {   
            if(
               (elem.state() == state) ||
               !state
              )
                TrelloApi.del("cards/"+this.card().id()+"/checkItem/"+elem.data.id);
        }.bind(this));
        this.item_list = null;
        return this;
    }

    /**
    * Mark all items incomplete
    * @memberof module:TrelloEntities.Checklist
    * @example
    * card.checklist("Something").reset();
    */
    this.reset = function()
    {
        this.items().each(function(elem)
        {   
            if(elem.state() == "complete")
                TrelloApi.put("cards/"+this.card().data.id+"/checkItem/"+elem.data.id+"?state=incomplete");
        }.bind(this));
        this.item_list = null;
        return this;
    }
    
    /**
    * Remove this checklist from the containing card
    * @memberof module:TrelloEntities.Checklist
    * @example
    * card.checklist("Something").remove();
    */
    this.remove = function()
    {
      TrelloApi.del("cards/"+this.card().id()+"/checklists/"+this.id());
      return this.card();
    }

    /**
    * Mark all items complete
    * @memberof module:TrelloEntities.Checklist
    * @example
    * card.checklist("Something").markAllItemsComplete();
    */
    this.markAllItemsComplete = function()
    {
      this.items().each(function(elem)
                        {   
                          if(elem.state() == "incomplete")
                            TrelloApi.put("cards/"+this.card().id()+"/checkItem/"+elem.id()+"?state=complete");
                        }.bind(this));
      
      this.item_list = null;
      return this;
    }

    /**
    * Convert this checklist into a list of links 
    * to newly created cards in the given list, with
    * each card being named after the original checklist
    * item and each checklist item being linked to the
    * newly created card. Each newly created card
    * has a link to the source card in the description.
    * @memberof module:TrelloEntities.Checklist
    * @param list {List} the list in which to create the new cards
    * @param params {Object} (optional) an optional set of key/value
    * pairs in an Object to use when creating the cards, the list
    * of allowed attributes is at {@link https://developers.trello.com/reference/#cards-2}
    * @example
    * //Convert into linked cards with each card having 
    * //the same labels and members as the original card
    * var member_ids = card.members().find(function(member)
    * {
    *     return member.id();
    * }).implodeValues(",");
    * var label_ids = card.labels().find(function(label)
    * {
    *     return label.id();
    * }).implodeValues(",");
    * card.checklist("Something")
    * .convertIntoLinkedCards(card.board().findOrCreateList(card.name()+" Linked Cards"),
    *                         {idLabels: label_ids,
    *                          idMembers: member_ids});
    */
    this.convertIntoLinkedCards = function(list,params)
    {
        if(!params)
            params = {};

        params.desc = this.card().link();

        this.items().each(function(item)
        {
            params.name = item.name();
            item.setName(Card.create(list,params).link());
        }.bind(this));
        this.item_list = null;
        return this;
    }

    /**
    * Return true if all items in this checklist 
    * are complete
    * @memberof module:TrelloEntities.Checklist
    * @example
    * if(card.checklist("Something").isComplete())
    *     card.postComment("Something is complete");
    */
    this.isComplete = function()
    {
        var ret = true;

        if(!this.data.checkItems)
            this.load();
      //If we don't have any checkitems for some reason, then we are by definition complete
      if(!this.data.checkItems)
        return true;
        
        new IterableCollection(this.data.checkItems).each(function(elem)
        {
            if(elem.state == "incomplete")
                ret = false;
        });
            
        return ret;
    }

    /**
    * Return the card that this checklist is on
    * @memberof module:TrelloEntities.Checklist
    * @example
    * new Notification(posted).completedChecklist().card().postComment("Well done!");
    */
    this.card = function()
    {
        return this.containing_card;
    }

    /**
    * Return the name of this checklist
    * @memberof module:TrelloEntities.Checklist
    * @example
    * card.postComment(new Notification(posted).completedChecklist().name()+" was completed");
    */
    this.name = function()
    {
        if(!this.data.name && !this.data.text)
            this.load();
        
        return this.data.name ? this.data.name:this.data.text;
    }

    /**
    * Add an item to this checklist if an item with the
    * same name does not already exist
    * @memberof module:TrelloEntities.Checklist
    * @param name {string} the name for the checklist item
    * @param position {int} (optional) where to add the item
    * @example
    * card.addChecklist("Some List",function(list)
    * {
    *     list.addUniqueItem(card.link());
    * });
    */
    this.addUniqueItem = function(name,position,checked)
    {
        try
        {
            var existing = this.items().findByName(name).first();
            
            if(checked)
                existing.markComplete();
        }
        
        catch(e)
        {
            Notification.expectException(InvalidDataException,e);
            this.addItem(name,position,checked);
        }
        
        return this;
    }

    /**
    * Add an item to this checklist, even if it 
    * already exists
    * @memberof module:TrelloEntities.Checklist
    * @param name {string} the name for the checklist item
    * @param position {int} (optional) where to add the item
    * @example
    * card.addChecklist("Some List",function(list)
    * {
    *     list.addItem(card.link());
    * });
    */
    this.addItem = function(name,position,checked)
    {
        if(!position)
            position = "bottom";

        new CheckItem(TrelloApi.post("checklists/"+this.data.id+"/checkItems?name="+encodeURIComponent(name)+"&pos="+encodeURIComponent(position))+"&checked="+checked).setContainingChecklist(this);
        this.item_list = null;
        return this;
    }
    
    /**
    * Return an item from this checklist by name
    * or RegExp
    * @memberof module:TrelloEntities.Checklist
    * @param name {string|RegExp} the name or regex to match against the
    * item name
    * @example
    * card.checklist("Something").item(new RegExp("Iain's.*")).markComplete();
    */
    this.item = function(name)
    {
        return this.items(name).first();
    }

    /**
    * Return an IterableCollection of checklist items
    * optionally filered by name or RegExp
    * @memberof module:TrelloEntities.Checklist
    * @param name {string|RegExp} filter the list by string or RegExp
    * @example
    * card.checklist("Something").items(new RegExp("Iain's.*")).each(function(item)
    * {
    *     item.remove();
    * });
    */
    this.items = function(name)
    {
        if(!this.item_list)
        {
            this.item_list = new IterableCollection(TrelloApi.get("checklists/"+this.data.id+"/checkItems")).transform(function(item)
            {
                return new CheckItem(item).setContainingChecklist(this);
            }.bind(this));
        }

        return this.item_list.findByName(name);
    }
    
    //INTERNAL USE ONLY
    this.load = function()
    {
        this.data = TrelloApi.get("checklists/"+this.data.id+"?cards=all&checkItems=all&checkItem_fields=all&fields=all");
      
      if(!this.data)
        throw new InvalidDataException("We appear to not exist ... ");
      
        return this;
    }

    //INTERNAL USE ONLY
    this.setContainingCard = function(card)
    {
        this.containing_card = card;
        return this;
    }
}