<commit_before>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';

var parkMap = {
  acad: 'Acadia National Park',
  arch: 'Arches National Park',
  badl: 'Badlands National Park',
  brca: 'Bryce Canyon National Park',
  crla: 'Crater Lake National Park',
  dena: 'Denali National Park',
  glac: 'Glacier National Park',
  grca: 'Grand Canyon National Park',
  grte: 'Grand Teton National Park',
  olym: 'Olympic National Park',
  yell: 'Yellowstone National Park',
  yose: 'Yosemite National Park',
  zion: 'Zion National Park'
};

var funcMap = {
  sndSlkDm: 'Send DM to assignee through Slack.'
}

var reqMap = {
  sndSlkDm: 'dm'
}

var currentCard = ''
var currentBoard = ''

var getBadges = function(t){
  return t.card('name')
  .get('name')
  .then(function(cardName){
    var badgeColor;
    var icon = GRAY_ICON;
    var lowercaseName = cardName.toLowerCase();
    if(lowercaseName.indexOf('green') > -1){
      badgeColor = 'green';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('yellow') > -1){
      badgeColor = 'yellow';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('red') > -1){
      badgeColor = 'red';
      icon = WHITE_ICON;
    }

    if(lowercaseName.indexOf('dynamic') > -1){
      // dynamic badges can have their function rerun after a set number
      // of seconds defined by refresh. Minimum of 10 seconds.
      return [{
        dynamic: function(){
          return {
            title: 'Detail Badge', // for detail badges only
            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
            icon: icon, // for card front badges only
            color: badgeColor,
            refresh: 10
          }
        }
      }]
    }

    if(lowercaseName.indexOf('static') > -1){
      // return an array of badge objects
      return [{
        title: 'Detail Badge', // for detail badges only
        text: 'Static',
        icon: icon, // for card front badges only
        color: badgeColor
      }];
    } else {
      return [];
    }
  })
};

var formatNPSUrl = function(t, url){
  if(!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)){
    return null;
  }
  var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
  if(parkShort && parkMap[parkShort]){
    return parkMap[parkShort];
  } else{
    return null;
  }
};

var boardButtonCallback = function(t){
  return t.popup({
    title: 'Popup List Example',
    items: [
      {
        text: 'Open Overlay',
        callback: function(t){
          return t.overlay({
            url: './overlay.html',
            args: { rand: (Math.random() * 100).toFixed(0) }
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Open Board Bar',
        callback: function(t){
          return t.boardBar({
            url: './board-bar.html',
            height: 200
          })
          .then(function(){
            return t.closePopup();
          });
        }
      }
    ]
  });
};

/*
var cardButtonCallback = function(t, options){
  var items = Object.keys(funcMap).map(function(funcCode){
    var urlForCode = 'http://serene-stream-62085.herokuapp.com/' + funcCode + '/';
    return {
      text: funcMap[funcCode],
      url: urlForCode,
      callback: function(t){
        return t.attach({ url: urlForCode, name: funcMap[funcCode] })
        .then(function(){
          return t.closePopup();
        })
      }
    };
  });

  return t.popup({
    title: 'Functions',
    items: items,
    search: {
      count: 1,
      placeholder: 'Select function...',
      empty: 'No function found'
    }
  });
};
*/

var cardButtonCallback = function(t, options){
  var items = Object.keys(funcMap).map(function(funcCode){
    var urlForCode = 'http://serene-stream-62085.herokuapp.com/dm';
    return {
      text: funcMap[funcCode],
      url: urlForCode,
      callback: function (urlForCode, options) {
          method = "post"; // Set method to post by default if not specified.

          // The rest of this code assumes you are not using a library.
          // It can be made less wordy if you use one.
          var form = document.createElement("form");
          form.setAttribute("method", method);
          form.setAttribute("action", urlForCode);

          for(var key in options) {
              if(options.hasOwnProperty(key)) {
                  var hiddenField = document.createElement("input");
                  hiddenField.setAttribute("card", currentCard);
                  hiddenField.setAttribute("board", currentBoard);

                  form.appendChild(hiddenField);
              }
          }

          document.body.appendChild(form);
          form.submit();
      }
    };
  });

  return t.popup({
    title: 'Functions',
    items: items,
    search: {
      count: 1,
      placeholder: 'Select function...',
      empty: 'No function found'
    }
  });
};

TrelloPowerUp.initialize({
  'attachment-sections': function(t, options){
    // options.entries is a list of the attachments for this card
    // you can look through them and 'claim' any that you want to
    // include in your section.

    // we will just claim urls for Yellowstone
    var claimed = options.entries.filter(function(attachment){
      return attachment.url.indexOf('http://www.nps.gov/yell/') == 0;
    });

    // you can have more than one attachment section on a card
    // you can group items together into one section, have a section
    // per attachment, or anything in between.
    if(claimed && claimed.length > 0){
      // if the title for your section requires a network call or other
      // potentially length operation you can provide a function for the title
      // that returns the section title. If you do so, provide a unique id for
      // your section
      return [{
        id: 'Yellowstone', // optional if you aren't using a function for the title
        claimed: claimed,
        icon: GRAY_ICON,
        title: 'Example Attachment Section: Yellowstone',
        content: {
          type: 'iframe',
          url: t.signUrl('./section.html', { arg: 'you can pass your section args here' }),
          height: 230
        }
      }];
    } else {
      return [];
    }
  },
  'attachment-thumbnail': function(t, options){
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      // return an object with some or all of these properties:
      // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
      return {
        url: options.url,
        title: parkName,
        image: {
          url: './images/nps.svg',
          logo: true // false if you are using a thumbnail of the content
        },
        openText: 'Open in NPS'
      };
    } else {
      throw t.NotHandled();
    }
  },
  'board-buttons': function(t, options){
    return [{
      icon: WHITE_ICON,
      text: 'Aurender',
      callback: boardButtonCallback
    }];
  },
  'card-badges': function(t, options){
    return getBadges(t);
  },
  'card-buttons': function(t, options) {
    currentCard = options["context"]["card"];
    currentBoard = options["context"]["board"];
    return [{
      icon: GRAY_ICON,
      text: 'Aurender',
      callback: cardButtonCallback
    }];
  },
  'card-detail-badges': function(t, options) {
    return getBadges(t);
  },
  'card-from-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        name: parkName,
        desc: 'An awesome park: ' + options.url
      };
    } else {
      throw t.NotHandled();
    }
  },
  'format-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        icon: GRAY_ICON,
        text: parkName
      };
    } else {
      throw t.NotHandled();
    }
  },
  'show-settings': function(t, options){
    return t.popup({
      title: 'Settings',
      url: './settings.html',
      height: 184
    });
  }
});
<commit_msg>add variable for url<commit_after>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';

var parkMap = {
  acad: 'Acadia National Park',
  arch: 'Arches National Park',
  badl: 'Badlands National Park',
  brca: 'Bryce Canyon National Park',
  crla: 'Crater Lake National Park',
  dena: 'Denali National Park',
  glac: 'Glacier National Park',
  grca: 'Grand Canyon National Park',
  grte: 'Grand Teton National Park',
  olym: 'Olympic National Park',
  yell: 'Yellowstone National Park',
  yose: 'Yosemite National Park',
  zion: 'Zion National Park'
};

var funcMap = {
  sndSlkDm: 'Send DM to assignee through Slack.'
}

var reqMap = {
  sndSlkDm: 'dm'
}

var currentCard = ''
var currentBoard = ''
var currentUrl = ''

var getBadges = function(t){
  return t.card('name')
  .get('name')
  .then(function(cardName){
    var badgeColor;
    var icon = GRAY_ICON;
    var lowercaseName = cardName.toLowerCase();
    if(lowercaseName.indexOf('green') > -1){
      badgeColor = 'green';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('yellow') > -1){
      badgeColor = 'yellow';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('red') > -1){
      badgeColor = 'red';
      icon = WHITE_ICON;
    }

    if(lowercaseName.indexOf('dynamic') > -1){
      // dynamic badges can have their function rerun after a set number
      // of seconds defined by refresh. Minimum of 10 seconds.
      return [{
        dynamic: function(){
          return {
            title: 'Detail Badge', // for detail badges only
            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
            icon: icon, // for card front badges only
            color: badgeColor,
            refresh: 10
          }
        }
      }]
    }

    if(lowercaseName.indexOf('static') > -1){
      // return an array of badge objects
      return [{
        title: 'Detail Badge', // for detail badges only
        text: 'Static',
        icon: icon, // for card front badges only
        color: badgeColor
      }];
    } else {
      return [];
    }
  })
};

var formatNPSUrl = function(t, url){
  if(!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)){
    return null;
  }
  var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
  if(parkShort && parkMap[parkShort]){
    return parkMap[parkShort];
  } else{
    return null;
  }
};

var boardButtonCallback = function(t){
  return t.popup({
    title: 'Popup List Example',
    items: [
      {
        text: 'Open Overlay',
        callback: function(t){
          return t.overlay({
            url: './overlay.html',
            args: { rand: (Math.random() * 100).toFixed(0) }
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Open Board Bar',
        callback: function(t){
          return t.boardBar({
            url: './board-bar.html',
            height: 200
          })
          .then(function(){
            return t.closePopup();
          });
        }
      }
    ]
  });
};

/*
var cardButtonCallback = function(t, options){
  var items = Object.keys(funcMap).map(function(funcCode){
    var urlForCode = 'http://serene-stream-62085.herokuapp.com/' + funcCode + '/';
    return {
      text: funcMap[funcCode],
      url: urlForCode,
      callback: function(t){
        return t.attach({ url: urlForCode, name: funcMap[funcCode] })
        .then(function(){
          return t.closePopup();
        })
      }
    };
  });

  return t.popup({
    title: 'Functions',
    items: items,
    search: {
      count: 1,
      placeholder: 'Select function...',
      empty: 'No function found'
    }
  });
};
*/

var cardButtonCallback = function(t, options){
  var items = Object.keys(funcMap).map(function(funcCode){
    var urlForCode = 'http://serene-stream-62085.herokuapp.com/dm';
    currentUrl = urlForCode;
    return {
      text: funcMap[funcCode],
      url: urlForCode,
      callback: function (urlForCode, options) {
          method = "post"; // Set method to post by default if not specified.

          // The rest of this code assumes you are not using a library.
          // It can be made less wordy if you use one.
          var form = document.createElement("form");
          form.setAttribute("method", method);
          form.setAttribute("action", currentUrl);

          for(var key in options) {
              if(options.hasOwnProperty(key)) {
                  var hiddenField = document.createElement("input");
                  hiddenField.setAttribute("card", currentCard);
                  hiddenField.setAttribute("board", currentBoard);

                  form.appendChild(hiddenField);
              }
          }

          document.body.appendChild(form);
          form.submit();
      }
    };
  });

  return t.popup({
    title: 'Functions',
    items: items,
    search: {
      count: 1,
      placeholder: 'Select function...',
      empty: 'No function found'
    }
  });
};

TrelloPowerUp.initialize({
  'attachment-sections': function(t, options){
    // options.entries is a list of the attachments for this card
    // you can look through them and 'claim' any that you want to
    // include in your section.

    // we will just claim urls for Yellowstone
    var claimed = options.entries.filter(function(attachment){
      return attachment.url.indexOf('http://www.nps.gov/yell/') == 0;
    });

    // you can have more than one attachment section on a card
    // you can group items together into one section, have a section
    // per attachment, or anything in between.
    if(claimed && claimed.length > 0){
      // if the title for your section requires a network call or other
      // potentially length operation you can provide a function for the title
      // that returns the section title. If you do so, provide a unique id for
      // your section
      return [{
        id: 'Yellowstone', // optional if you aren't using a function for the title
        claimed: claimed,
        icon: GRAY_ICON,
        title: 'Example Attachment Section: Yellowstone',
        content: {
          type: 'iframe',
          url: t.signUrl('./section.html', { arg: 'you can pass your section args here' }),
          height: 230
        }
      }];
    } else {
      return [];
    }
  },
  'attachment-thumbnail': function(t, options){
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      // return an object with some or all of these properties:
      // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
      return {
        url: options.url,
        title: parkName,
        image: {
          url: './images/nps.svg',
          logo: true // false if you are using a thumbnail of the content
        },
        openText: 'Open in NPS'
      };
    } else {
      throw t.NotHandled();
    }
  },
  'board-buttons': function(t, options){
    return [{
      icon: WHITE_ICON,
      text: 'Aurender',
      callback: boardButtonCallback
    }];
  },
  'card-badges': function(t, options){
    return getBadges(t);
  },
  'card-buttons': function(t, options) {
    currentCard = options["context"]["card"];
    currentBoard = options["context"]["board"];
    return [{
      icon: GRAY_ICON,
      text: 'Aurender',
      callback: cardButtonCallback
    }];
  },
  'card-detail-badges': function(t, options) {
    return getBadges(t);
  },
  'card-from-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        name: parkName,
        desc: 'An awesome park: ' + options.url
      };
    } else {
      throw t.NotHandled();
    }
  },
  'format-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        icon: GRAY_ICON,
        text: parkName
      };
    } else {
      throw t.NotHandled();
    }
  },
  'show-settings': function(t, options){
    return t.popup({
      title: 'Settings',
      url: './settings.html',
      height: 184
    });
  }
});
<|endoftext|>


<commit_before>console.log = function() {};

var spriteful = {
  loaded_sprites: {},

  find_path: function(start, end, path) {
    // Eventually implement this as A*

    /*
    console.log(_.template('Finding path from (<%= start.row %>, <%= start.col %>) to (<%= end.row %>,<%= end.col %>)',
      {
        start: start,
        end: end
      }
    ));
    */

    var equals = function(a, b) { return (a.row == b.row && a.col == b.col) };
    var new_path = path;

    if (equals(start, end)) {
      new_path.push(end);
      return path;
    } else {
      var step;
      if (end.row > start.row) {
        step = {row: 1, col: 0};
      } else if (end.row < start.row) {
        step = {row: -1, col: 0};
      } else if (end.col > start.col) {
        step = {row: 0, col: 1};
      } else if (end.col < start.col) {
        step = {row:0, col: -1};
      }

      next = {
        row: start.row + step.row,
        col: start.col + step.col
      };

      new_path.push(start);

      return spriteful.find_path(next, end, new_path);
    }
  },

  move_action: function(ref, node) {
    var $this = $(ref);
    console.log(_.template('<%= id %> is moving to [<%= row %>, <%= col %>]', {
      id: $this.selector,
      row: node.data('row'),
      col: node.data('col')
    }));
    $this.orientTowards(node).advanceAnimation();

    var cloned = $this.detach();

    $(cloned)
      .data('row',        node.data('row'))
      .data('col',        node.data('col'));

    $(node).append(cloned);

  },

  parse_sprite_image: function(img_url) {
    var parsed = $.url.setUrl(img_url).attr("file").split('.');
    var dimensions = parsed[2].split('-');
    var sprite = {
      type: parsed[0],
      animation: parsed[1],
      width: dimensions[0],
      height: dimensions[1],
      frames: dimensions[2],
      url: img_url,
      num: 0
    };

    var key = [sprite.type, sprite.animation].join('.');
    if (!spriteful.loaded_sprites[key]) {
      $(_.template(spriteful.templates.sprite_css, sprite)).appendTo('head');
      spriteful.loaded_sprites[key] = true;
    }
    return sprite;
  },

  templates: {
    'sprite_css': [
      ""
    ].join("\n")
  }
};

(function($) {
  $.fn.spriteful = function(config) {
    $(this).data('config', config)
           .addClass('_controller')
           .initTiles(config)
           .initClock('.' + config.actor_class, config.heartbeat_interval);
  };

  $.fn.spritefulConfig = function() {
    return $(this).parents('._controller').data('config');
  }

  $.fn.placeSprite = function(options) {
    //id, classes, sprite_options
    var $this = $(this);
    var config = $this.spritefulConfig();
    var classes = options.other_classes;
    classes.push(options.main_class);
    classes.push(config.actor_class);
    classes.push(config.sprite_class);
    classes.push(options.starting_sprite + '-0');

    var sprite_html = _.template('<div id="<%= id %>" class="<%= classes %>">
', {
      id: options.id,
      classes: classes.join(" ")
    });
    var el = $(sprite_html);

    $this.append(el);

    var sprite_options = spriteful.parse_sprite_image(options.sprites[options.starting_sprite]);
    $('#' + options.id)
      .data('animation', sprite_options)
      .data('row', $this.data('row'))
      .data('col', $this.data('col'))
      .data('move_func', spriteful.move_action)
      .data('sprites', options.sprites);

    return $this;
  }

  $.fn.pathTo = function(end_selector) {
    var $this = $(this);
    var $dest = $(end_selector);

    var start = {
      row: $this.data('row'),
      col: $this.data('col'),
    };

    var end = {
      row: $dest.data('row'),
      col: $dest.data('col')
    }

    var path = spriteful.find_path(start, end, []);
    var config = $this.spritefulConfig();

    return $(_(path).map(function(o) {
      o._tile_class = config.tile_class;
      var sel = _.template('.<%= _tile_class %>.row-<%= row %>.col-<%= col %>', o);
      return $(sel);
    }));
  };

  $.fn.moveTo = function(end_selector) {
    return $(this).each(function() {
      var $this = $(this);
      var move_func = $this.data('move_func');
      var intentions = _($this.pathTo(end_selector)).map(function(node) {
        return function() { move_func($this, node) };
      });
      $this.data('intentions', intentions);
    })
  };

  $.fn.orientTowards = function(target_selector) {
    var $this = $(this);
    var $target = $(target_selector);

    var prev = {
      row: $this.data('row'),
      col: $this.data('col')
    }
    var row = $target.data('row');
    var col = $target.data('col');

    if (col > prev.col || row > prev.row) {
      $this.removeClass('facing-left').addClass('facing-right');
    } else if (col < prev.col || row < prev.row) {
      $this.removeClass('facing-right').addClass('facing-left');
    }

    return $this;
  },

  $.fn.advanceAnimation = function() {
    var $this = $(this);
    var old_animation = $this.data('animation');
    var old_class = [old_animation.animation, old_animation.num].join('-');

    var new_animation = old_animation;
    new_animation.num = (new_animation.num + 1) % new_animation.frames;
    var new_class = [new_animation.animation, new_animation.num].join('-');

    $this.removeClass(old_class).addClass(new_class).data('animation', new_animation);
  }

  $.fn.initTiles = function(config) {
    var tile_class = config.tile_class;
    var sprite_class = config.sprite_class;
    var board = config.board;

    var getRowNum = function(i, total_cols) {
      return Math.floor(i / total_cols);
    };
    var getColNum = function(i, total_cols) {
      return Math.floor(i % total_cols);
    }

    var tile_count = board.rows * board.cols;

    $(_.template(
      [""].join("\n"), {
      board_selector: $(this).selector,
      board_width: board.tile_width * board.cols,
      board_height: board.tile_height * board.rows,
      tile_class: tile_class,
      tile_width: board.tile_width,
      tile_height: board.tile_height,
      sprite_class: sprite_class
    })).appendTo('head');

    var tiles = _.range(tile_count).map(function(i) {
      var coordinates = {
        row: getRowNum(i, board.cols),
        col: getColNum(i, board.cols),
        tile_class: tile_class
      };

      return _.template('<div class="<%= tile_class %> row-<%= row %> col-<%= col %>" id="cell-<%= row %>-<%= col %>">
', coordinates)
    }).join("");

    $(this).html(tiles);

    $('.' + tile_class).each(function() {
      var $this = $(this);
      if(!$this.data('row')) {
        var id_parts = $this.attr('id').split('-');
        $this.data('row', parseInt(id_parts[1])).data('col', parseInt(id_parts[2]));
      }
    });

    return $(this);
  };

  $.fn.initClock = function(actor_selector, beat_interval) {
    var $this = $(this);
    var heartbeat = function() {
      $this.find(actor_selector).trigger('g:heartbeat');
      window.setTimeout(heartbeat, beat_interval);
    };

    window.setTimeout(heartbeat, beat_interval);
    $this.find(actor_selector).live('g:heartbeat', function() {
      var intentions = $(this).data('intentions');

      if (!_.isEmpty(intentions)) {
        var next = _(intentions).first();
        $(this).data('intentions', _(intentions).slice(1, intentions.length));
        console.log('#' + $(this).attr('id') + ' has ' + (intentions.length - 1) + ' intentions left');
        next();
      } else {
        $(this).trigger('g:idle');
      }
    });

    return $(this);
  };

})(jQuery);

<commit_msg>allow moving in diagonals<commit_after>console.log = function() {};

var spriteful = {
  loaded_sprites: {},

  find_path: function(start, end, path) {
    // Eventually implement this as A*

    /*
    console.log(_.template('Finding path from (<%= start.row %>, <%= start.col %>) to (<%= end.row %>,<%= end.col %>)',
      {
        start: start,
        end: end
      }
    ));
    */

    var equals = function(a, b) { return (a.row == b.row && a.col == b.col) };
    var new_path = path;

    if (equals(start, end)) {
      new_path.push(end);
      return path;
    } else {
      var step = {row:0, col: 0};
      if (end.row > start.row)      { step.row += 1 }
      else if (end.row < start.row) { step.row -= 1 }
      if (end.col > start.col)      { step.col += 1 }
      else if (end.col < start.col) { step.col -= 1 }

      next = {
        row: start.row + step.row,
        col: start.col + step.col
      };

      new_path.push(start);

      return spriteful.find_path(next, end, new_path);
    }
  },

  move_action: function(ref, node) {
    var $this = $(ref);
    console.log(_.template('<%= id %> is moving to [<%= row %>, <%= col %>]', {
      id: $this.selector,
      row: node.data('row'),
      col: node.data('col')
    }));
    $this.orientTowards(node).advanceAnimation();

    var cloned = $this.detach();

    $(cloned)
      .data('row',        node.data('row'))
      .data('col',        node.data('col'));

    $(node).append(cloned);

  },

  parse_sprite_image: function(img_url) {
    var parsed = $.url.setUrl(img_url).attr("file").split('.');
    var dimensions = parsed[2].split('-');
    var sprite = {
      type: parsed[0],
      animation: parsed[1],
      width: dimensions[0],
      height: dimensions[1],
      frames: dimensions[2],
      url: img_url,
      num: 0
    };

    var key = [sprite.type, sprite.animation].join('.');
    if (!spriteful.loaded_sprites[key]) {
      $(_.template(spriteful.templates.sprite_css, sprite)).appendTo('head');
      spriteful.loaded_sprites[key] = true;
    }
    return sprite;
  },

  templates: {
    'sprite_css': [
      ""
    ].join("\n")
  }
};

(function($) {
  $.fn.spriteful = function(config) {
    $(this).data('config', config)
           .addClass('_controller')
           .initTiles(config)
           .initClock('.' + config.actor_class, config.heartbeat_interval);
  };

  $.fn.spritefulConfig = function() {
    return $(this).parents('._controller').data('config');
  }

  $.fn.placeSprite = function(options) {
    //id, classes, sprite_options
    var $this = $(this);
    var config = $this.spritefulConfig();
    var classes = options.other_classes;
    classes.push(options.main_class);
    classes.push(config.actor_class);
    classes.push(config.sprite_class);
    classes.push(options.starting_sprite + '-0');

    var sprite_html = _.template('<div id="<%= id %>" class="<%= classes %>">
', {
      id: options.id,
      classes: classes.join(" ")
    });
    var el = $(sprite_html);

    $this.append(el);

    var sprite_options = spriteful.parse_sprite_image(options.sprites[options.starting_sprite]);
    $('#' + options.id)
      .data('animation', sprite_options)
      .data('row', $this.data('row'))
      .data('col', $this.data('col'))
      .data('move_func', spriteful.move_action)
      .data('sprites', options.sprites);

    return $this;
  }

  $.fn.pathTo = function(end_selector) {
    var $this = $(this);
    var $dest = $(end_selector);

    var start = {
      row: $this.data('row'),
      col: $this.data('col'),
    };

    var end = {
      row: $dest.data('row'),
      col: $dest.data('col')
    }

    var path = spriteful.find_path(start, end, []);
    var config = $this.spritefulConfig();

    return $(_(path).map(function(o) {
      o._tile_class = config.tile_class;
      var sel = _.template('.<%= _tile_class %>.row-<%= row %>.col-<%= col %>', o);
      return $(sel);
    }));
  };

  $.fn.moveTo = function(end_selector) {
    return $(this).each(function() {
      var $this = $(this);
      var move_func = $this.data('move_func');
      var intentions = _($this.pathTo(end_selector)).map(function(node) {
        return function() { move_func($this, node) };
      });
      $this.data('intentions', intentions);
    })
  };

  $.fn.orientTowards = function(target_selector) {
    var $this = $(this);
    var $target = $(target_selector);

    var prev = {
      row: $this.data('row'),
      col: $this.data('col')
    }
    var row = $target.data('row');
    var col = $target.data('col');

    if (col > prev.col || row > prev.row) {
      $this.removeClass('facing-left').addClass('facing-right');
    } else if (col < prev.col || row < prev.row) {
      $this.removeClass('facing-right').addClass('facing-left');
    }

    return $this;
  },

  $.fn.advanceAnimation = function() {
    var $this = $(this);
    var old_animation = $this.data('animation');
    var old_class = [old_animation.animation, old_animation.num].join('-');

    var new_animation = old_animation;
    new_animation.num = (new_animation.num + 1) % new_animation.frames;
    var new_class = [new_animation.animation, new_animation.num].join('-');

    $this.removeClass(old_class).addClass(new_class).data('animation', new_animation);
  }

  $.fn.initTiles = function(config) {
    var tile_class = config.tile_class;
    var sprite_class = config.sprite_class;
    var board = config.board;

    var getRowNum = function(i, total_cols) {
      return Math.floor(i / total_cols);
    };
    var getColNum = function(i, total_cols) {
      return Math.floor(i % total_cols);
    }

    var tile_count = board.rows * board.cols;

    $(_.template(
      [""].join("\n"), {
      board_selector: $(this).selector,
      board_width: board.tile_width * board.cols,
      board_height: board.tile_height * board.rows,
      tile_class: tile_class,
      tile_width: board.tile_width,
      tile_height: board.tile_height,
      sprite_class: sprite_class
    })).appendTo('head');

    var tiles = _.range(tile_count).map(function(i) {
      var coordinates = {
        row: getRowNum(i, board.cols),
        col: getColNum(i, board.cols),
        tile_class: tile_class
      };

      return _.template('<div class="<%= tile_class %> row-<%= row %> col-<%= col %>" id="cell-<%= row %>-<%= col %>">
', coordinates)
    }).join("");

    $(this).html(tiles);

    $('.' + tile_class).each(function() {
      var $this = $(this);
      if(!$this.data('row')) {
        var id_parts = $this.attr('id').split('-');
        $this.data('row', parseInt(id_parts[1])).data('col', parseInt(id_parts[2]));
      }
    });

    return $(this);
  };

  $.fn.initClock = function(actor_selector, beat_interval) {
    var $this = $(this);
    var heartbeat = function() {
      $this.find(actor_selector).trigger('g:heartbeat');
      window.setTimeout(heartbeat, beat_interval);
    };

    window.setTimeout(heartbeat, beat_interval);
    $this.find(actor_selector).live('g:heartbeat', function() {
      var intentions = $(this).data('intentions');

      if (!_.isEmpty(intentions)) {
        var next = _(intentions).first();
        $(this).data('intentions', _(intentions).slice(1, intentions.length));
        console.log('#' + $(this).attr('id') + ' has ' + (intentions.length - 1) + ' intentions left');
        next();
      } else {
        $(this).trigger('g:idle');
      }
    });

    return $(this);
  };

})(jQuery);

<|endoftext|>


<commit_before>
// The textbook is stored in brackets.currentTextbook

function createNewTextbook() {
	var newTextbook 		= {};
	newTextbook.title 		= "Title";
	newTextbook.author 		= "Author";
	newTextbook.elements 	= [];
	return newTextbook;
}

function textbookIsWellDefined(textbook) {
	if (typeof(textbook.title) != "string") {
		return false;
	}
	else if (typeof(textbook.author) != "string") {
		return false;
	}
	else if (!(textbook.elements.isArray)) {
		return false;
	}
	else {
		var numberOfElements = textbook.elements.length;
		for (var el = 0; el < numberOfElements; el++) {
			if (!(elementIsWellDefined(textbook.elements[el]))) return false;
		}
		return true;
	}
}

function elementIsWellDefined(element) {
	// Check that element has a type that is a string
	if (typeof(element.type) != "string") {
		return false;
	}
	// Check that element has comments
	else if (!(element.comments.isArray)) {
		return false;
	}

	else {
		// Check that comments are well defined
		var numberOfComments = element.comments.length;
		for (var comm = 0; comm < numberOfComments; comm++) {
			if (!(commentIsWellDefined(element.comments[comm]))) return false;
		}
		// Check that rest is well defined for the different types of elements
		switch(element.type) {
			case 'pdfRectangle':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				// Check that element has all coordinates
				else if (typeof(element.startX) != "number" ||
						 typeof(element.startY) != "number" ||
						 typeof(element.endX) != "number" ||
						 typeof(element.endY) != "number") {
					return false;
				}
				// Check that end is after start
				else if (element.startX >= element.endX ||
						 element.startY >= element.endY) {
					return false;
				}
				else {
					return true;
				}
			case 'pdfHorizontal':
			case 'youtube':
			case 'text' :
			case 'wikipedia':
			case 'image':
			default:
				return false;
				break;
		}
	}
}

function commentIsWellDefined(comment) {
	// Check that comment has an author that is a string
	if (typeof(comment.author) != "string") {
		return false;
	}
	// Check that comment has a comment that is a string
	else if (typeof(comment.comment) != "string") {
		return false;
	}
	else {
		return true;
	}
}

function modifyTitle(newTitle) {
	if (newTitle.length) {
		brackets.currentTextbook.title = newTitle;
	}
}

function modifyAuthor(newAuthor) {
	if (newAuthor.length) {
		brackets.currentTextbook.author = newAuthor;
	}
}

function addPDFRectangleClip(source,startX,startY,endX,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfRectangle";
	newElement.source 	= source;
	newElement.page		= Math.floor(startX);
	newElement.startX 	= startX;
	newElement.startY 	= startY;
	newElement.endX 	= endX;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addPDFRectangleClip(source,page,startX,startY,endX,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfRectangle";
	newElement.source 	= source;
	newElement.page		= page;
	newElement.startX 	= startX;
	newElement.startY 	= startY;
	newElement.endX 	= endX;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addPDFHorizontalClip(source,startY,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfHorizontal";
	newElement.source 	= source;
	newElement.startY 	= startY;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addYouTubeVid(source) {
	var newElement 		 = {};
	newElement.type 	 = "youtube";
	newElement.source 	 = source;
	newElement.comments  = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}
function addYouTubeVid(source, start, end) {
	var newElement 		 = {};
	newElement.type 	 = "youtube";
	newElement.source 	 = source;
	newElement.start 	 = start;
	newElement.end 		 = end;
	newElement.comments  = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addText(text) {
	var newElement 		= {};
	newElement.type 	= "text";
	newElement.text 	= text;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addWiki(source) {
	var newElement 		= {};
	newElement.type 	= "wikipedia";
	newElement.source 	= source;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addImage(source) {
	var newElement 		= {};
	newElement.type 	= "image";
	newElement.source 	= source;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
	renderElement(brackets.textbookContainer, newElement);
}

function previousPDFs() {
	var pdfLinks = [];
	var numberOfElements = brackets.currentTextbook.elements.length;
	for (var el = 0; el < numberOfElements; el++) {
		if (brackets.currentTextbook.elements[el].type == "pdfRectangle" ||
			brackets.currentTextbook.elements[el].type == "pdfHorizontal") {
			var tag = "PDF " + el;
			pdfLinks.push({
				tag: brackets.currentTextbook.elements[el].source
			})
		}
	};
	return pdfLinks;
}

// If within range, delete. Else skip
function deleteElement(clipNumber) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (clipNumber >= 0 && clipNumber<numberOfElements) {
		var newElements = [];
		for (var el = 0; el < clipNumber; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		for (var el = clipNumber+1; el < numberOfElements; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		brackets.currentTextbook.elements = newElements;
	}
}

// Move element at index oldPosition in between element at index newPosition and the element at index (newPosition+1)
function moveElement(oldPosition,newPosition) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (oldPosition >= 0 && oldPosition<numberOfElements &&
		newPosition >= 0 && newPosition<numberOfElements) {
		// If no change then return
		if (oldPosition == newPosition) {return;}
		// Copy everything that's not going to be changed
		var newElements = [];
		var firstChange = Math.min(oldPosition,newPosition);
		for (var el = 0; el < firstChange; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		// If move up the page
		if (oldPosition > newPosition) {
			newElements.append(brackets.currentTextbook.elements[oldPosition]);
			for (var el = newPosition; el < oldPosition; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			for (var el = oldPosition+1; el < numberOfElements; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			brackets.currentTextbook.elements = newElements;
		}
		// If move down the page
		else {
			for (var el = oldPosition+1; el <= newPosition; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			newElements.append(brackets.currentTextbook.elements[oldPosition]);
			for (var el = newPosition+1; el < numberOfElements; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			brackets.currentTextbook.elements = newElements;
		}
	};
}

// Comment functions below

function addComment(elementNumber,author,comment) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (elementNumber >= 0 && elementNumber<numberOfElements) {
		brackets.currentTextbook.elements[elementNumber].comments.append({
			"author"  : author,
			"comment" : comment
		});
	}
}

function deleteComment(elementNumber,commentNumber) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (elementNumber >= 0 && elementNumber<numberOfElements) {
		var numberOfComments = brackets.currentTextbook.elements[elementNumber].comments.length;
		if (commentNumber >= 0 && commentNumber<numberOfComments) {
			var newComments = [];
			for (var comm = 0; comm < commentNumber; el++) {
				newElements.add(brackets.currentTextbook.elements[elementNumber].comments[comm]);
			}
			for (var comm = commentNumber+1; comm < numberOfElements; el++) {
				newElements.add(brackets.currentTextbook.elements[elementNumber].comments[comm]);
			}
			brackets.currentTextbook.elements[elementNumber].comments = newComments;
		}
	}
}

function getCommentAuthors() {
	var authors = [];
	var numberOfElements = brackets.currentTextbook.elements.length;
	var numberOfComments = 0;
	for (var el = 0; el < numberOfElements; el++) {
		numberOfComments = brackets.currentTextbook.elements[el].comments.length;
		for (var comm = 0; comm < numberOfComments; comm++) {
			if (!(authors.contains(brackets.currentTextbook.elements[el].comments[comm].author))) {
				authors.append(brackets.currentTextbook.elements[el].comments[comm].author);
			}
		}
	}
	return authors;
}

var contains = function(needle) {
	contains = function(needle) {
		for (var i = 0; i < this.length; i++) {
			if(this[i] === needle) {
				return true;
			}
		}
	};
	return contains.call(this, needle);
}

function noCommentTextbook() {
	var virginTextbook = {
		"title": brackets.currentTextbook.title,
		"author": brackets.currentTextbook.author,
		"elements": []
	};
	brackets.currentTextbook.elements.forEach(function(element) {
		var virginElement = element;
		virginElement.comments = [];
		virginTextbook.elements.append(virginElement);
	});
	return virginTextbook;
}

function describeTextbook() {
	var textbookString = brackets.currentTextbook.name;
	textbookString += " by " + brackets.currentTextbook.author + "\n";
	var numberOfElements = brackets.currentTextbook.elements.length;
	for (int i = 0; i < length; i++) {
		textbookString += "\n" + i + ": ";
		switch(brackets.currentTextbook.elements[i].type) {
			case 'pdfRectangle':
				textbookString += "Rectangle from " + brackets.currentTextbook.elements[i].source;
			case 'pdfHorizontal':
				textbookString += "Horizontal clip from  " + brackets.currentTextbook.elements[i].source;
			case 'youtube':
				textbookString += "YouTube clip from  " + brackets.currentTextbook.elements[i].source;
			case 'text' :
				textbookString += "Some text";
			case 'wikipedia':
				textbookString += "Wikipedia clip from  " + brackets.currentTextbook.elements[i].source;
			case 'image':
				textbookString += "Image from  " + brackets.currentTextbook.elements[i].source;
			case default:
				textbookString += "Undefined type";
		}
	}
}
<commit_msg>finished testing function<commit_after>
// The textbook is stored in brackets.currentTextbook

function createNewTextbook() {
	var newTextbook 		= {};
	newTextbook.title 		= "Title";
	newTextbook.author 		= "Author";
	newTextbook.elements 	= [];
	return newTextbook;
}

function textbookIsWellDefined(textbook) {
	if (typeof(textbook.title) != "string") {
		return false;
	}
	else if (typeof(textbook.author) != "string") {
		return false;
	}
	else if (!(textbook.elements.isArray)) {
		return false;
	}
	else {
		var numberOfElements = textbook.elements.length;
		for (var el = 0; el < numberOfElements; el++) {
			if (!(elementIsWellDefined(textbook.elements[el]))) return false;
		}
		return true;
	}
}

function elementIsWellDefined(element) {
	// Check that element has a type that is a string
	if (typeof(element.type) != "string") {
		return false;
	}
	// Check that element has comments
	else if (!(element.comments.isArray)) {
		return false;
	}

	else {
		// Check that comments are well defined
		var numberOfComments = element.comments.length;
		for (var comm = 0; comm < numberOfComments; comm++) {
			if (!(commentIsWellDefined(element.comments[comm]))) return false;
		}
		// Check that rest is well defined for the different types of elements
		switch(element.type) {
			case 'pdfRectangle':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				// Check that element has all coordinates
				else if (typeof(element.startX) != "number" ||
						 typeof(element.startY) != "number" ||
						 typeof(element.endX) != "number" ||
						 typeof(element.endY) != "number" ||
						 typeof(element.page) != "number") {
					return false;
				}
				// Check that end is after start
				else if (element.startX >= element.endX ||
						 element.startY >= element.endY) {
					return false;
				}
				else {
					return true;
				}
			case 'pdfHorizontal':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				// Check that element has all coordinates
				else if (typeof(element.startY) != "number" ||
						 typeof(element.endY) != "number") {
					return false;
				}
				// Check that end is after start
				else if (element.startY >= element.endY) {
					return false;
				}
				else {
					return true;
				}
			case 'youtube':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				// Check that element has all coordinates
				else if (typeof(element.startTime) != "number") {
					return false;
				}
				// Check that startTime is after 0
				else if (element.startTime >= 0) {
					return false;
				}
				else {
					return true;
				}
			case 'text' :
				// Check that element has a text field
				if (typeof(element.text) != "string") {
					return false;
				}
				else {
					return true;
				}
			case 'wikipedia':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				else {
					return true;
				}
			case 'image':
				// Check that element has a source that is a string
				if (typeof(element.source) != "string") {
					return false;
				}
				else {
					return true;
				}
			default:
				return false;
				break;
		}
	}
}

function commentIsWellDefined(comment) {
	// Check that comment has an author that is a string
	if (typeof(comment.author) != "string") {
		return false;
	}
	// Check that comment has a comment that is a string
	else if (typeof(comment.comment) != "string") {
		return false;
	}
	else {
		return true;
	}
}

function modifyTitle(newTitle) {
	if (newTitle.length) {
		brackets.currentTextbook.title = newTitle;
	}
}

function modifyAuthor(newAuthor) {
	if (newAuthor.length) {
		brackets.currentTextbook.author = newAuthor;
	}
}

function addPDFRectangleClip(source,startX,startY,endX,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfRectangle";
	newElement.source 	= source;
	newElement.page		= Math.floor(startX);
	newElement.startX 	= startX;
	newElement.startY 	= startY;
	newElement.endX 	= endX;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addPDFRectangleClip(source,page,startX,startY,endX,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfRectangle";
	newElement.source 	= source;
	newElement.page		= page;
	newElement.startX 	= startX;
	newElement.startY 	= startY;
	newElement.endX 	= endX;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addPDFHorizontalClip(source,startY,endY) {
	var newElement 		= {};
	newElement.type 	= "pdfHorizontal";
	newElement.source 	= source;
	newElement.startY 	= startY;
	newElement.endY 	= endY;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addYouTubeVid(source) {
	var newElement 		 = {};
	newElement.type 	 = "youtube";
	newElement.source 	 = source;
	newElement.comments  = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}
function addYouTubeVid(source, start, end) {
	var newElement 		 = {};
	newElement.type 	 = "youtube";
	newElement.source 	 = source;
	newElement.start 	 = start;
	newElement.end 		 = end;
	newElement.comments  = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addText(text) {
	var newElement 		= {};
	newElement.type 	= "text";
	newElement.text 	= text;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addWiki(source) {
	var newElement 		= {};
	newElement.type 	= "wikipedia";
	newElement.source 	= source;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
    renderElement(brackets.textbookContainer, newElement);
}

function addImage(source) {
	var newElement 		= {};
	newElement.type 	= "image";
	newElement.source 	= source;
	newElement.comments = [];
	brackets.currentTextbook.elements.push(newElement);
	renderElement(brackets.textbookContainer, newElement);
}

function previousPDFs() {
	var pdfLinks = [];
	var numberOfElements = brackets.currentTextbook.elements.length;
	for (var el = 0; el < numberOfElements; el++) {
		if (brackets.currentTextbook.elements[el].type == "pdfRectangle" ||
			brackets.currentTextbook.elements[el].type == "pdfHorizontal") {
			var tag = "PDF " + el;
			pdfLinks.push({
				tag: brackets.currentTextbook.elements[el].source
			})
		}
	};
	return pdfLinks;
}

// If within range, delete. Else skip
function deleteElement(clipNumber) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (clipNumber >= 0 && clipNumber<numberOfElements) {
		var newElements = [];
		for (var el = 0; el < clipNumber; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		for (var el = clipNumber+1; el < numberOfElements; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		brackets.currentTextbook.elements = newElements;
	}
}

// Move element at index oldPosition in between element at index newPosition and the element at index (newPosition+1)
function moveElement(oldPosition,newPosition) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (oldPosition >= 0 && oldPosition<numberOfElements &&
		newPosition >= 0 && newPosition<numberOfElements) {
		// If no change then return
		if (oldPosition == newPosition) {return;}
		// Copy everything that's not going to be changed
		var newElements = [];
		var firstChange = Math.min(oldPosition,newPosition);
		for (var el = 0; el < firstChange; el++) {
			newElements.append(brackets.currentTextbook.elements[el]);
		}
		// If move up the page
		if (oldPosition > newPosition) {
			newElements.append(brackets.currentTextbook.elements[oldPosition]);
			for (var el = newPosition; el < oldPosition; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			for (var el = oldPosition+1; el < numberOfElements; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			brackets.currentTextbook.elements = newElements;
		}
		// If move down the page
		else {
			for (var el = oldPosition+1; el <= newPosition; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			newElements.append(brackets.currentTextbook.elements[oldPosition]);
			for (var el = newPosition+1; el < numberOfElements; el++) {
				newElements.append(brackets.currentTextbook.elements[el]);
			}
			brackets.currentTextbook.elements = newElements;
		}
	};
}

// Comment functions below

function addComment(elementNumber,author,comment) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (elementNumber >= 0 && elementNumber<numberOfElements) {
		brackets.currentTextbook.elements[elementNumber].comments.append({
			"author"  : author,
			"comment" : comment
		});
	}
}

function deleteComment(elementNumber,commentNumber) {
	var numberOfElements = brackets.currentTextbook.elements.length;
	if (elementNumber >= 0 && elementNumber<numberOfElements) {
		var numberOfComments = brackets.currentTextbook.elements[elementNumber].comments.length;
		if (commentNumber >= 0 && commentNumber<numberOfComments) {
			var newComments = [];
			for (var comm = 0; comm < commentNumber; el++) {
				newElements.add(brackets.currentTextbook.elements[elementNumber].comments[comm]);
			}
			for (var comm = commentNumber+1; comm < numberOfElements; el++) {
				newElements.add(brackets.currentTextbook.elements[elementNumber].comments[comm]);
			}
			brackets.currentTextbook.elements[elementNumber].comments = newComments;
		}
	}
}

function getCommentAuthors() {
	var authors = [];
	var numberOfElements = brackets.currentTextbook.elements.length;
	var numberOfComments = 0;
	for (var el = 0; el < numberOfElements; el++) {
		numberOfComments = brackets.currentTextbook.elements[el].comments.length;
		for (var comm = 0; comm < numberOfComments; comm++) {
			if (!(authors.contains(brackets.currentTextbook.elements[el].comments[comm].author))) {
				authors.append(brackets.currentTextbook.elements[el].comments[comm].author);
			}
		}
	}
	return authors;
}

var contains = function(needle) {
	contains = function(needle) {
		for (var i = 0; i < this.length; i++) {
			if(this[i] === needle) {
				return true;
			}
		}
	};
	return contains.call(this, needle);
}

function noCommentTextbook() {
	var virginTextbook = {
		"title": brackets.currentTextbook.title,
		"author": brackets.currentTextbook.author,
		"elements": []
	};
	brackets.currentTextbook.elements.forEach(function(element) {
		var virginElement = element;
		virginElement.comments = [];
		virginTextbook.elements.append(virginElement);
	});
	return virginTextbook;
}

function describeTextbook() {
	var textbookString = brackets.currentTextbook.name;
	textbookString += " by " + brackets.currentTextbook.author + "\n";
	var numberOfElements = brackets.currentTextbook.elements.length;
	for (int i = 0; i < length; i++) {
		textbookString += "\n" + i + ": ";
		switch(brackets.currentTextbook.elements[i].type) {
			case 'pdfRectangle':
				textbookString += "Rectangle from " + brackets.currentTextbook.elements[i].source;
			case 'pdfHorizontal':
				textbookString += "Horizontal clip from  " + brackets.currentTextbook.elements[i].source;
			case 'youtube':
				textbookString += "YouTube clip from  " + brackets.currentTextbook.elements[i].source;
			case 'text' :
				textbookString += "Some text";
			case 'wikipedia':
				textbookString += "Wikipedia clip from  " + brackets.currentTextbook.elements[i].source;
			case 'image':
				textbookString += "Image from  " + brackets.currentTextbook.elements[i].source;
			case default:
				textbookString += "Undefined type";
		}
	}
}
<|endoftext|>


<commit_before>import { run_all } from './utils.js';
import { set_current_component } from './lifecycle.js';

export let dirty_components = [];
export const intros = { enabled: false };

let update_promise;
const binding_callbacks = [];
const render_callbacks = [];

export function schedule_update() {
	if (!update_promise) {
		update_promise = Promise.resolve().then(flush);
	}
}

export function add_render_callback(fn) {
	render_callbacks.push(fn);
}

export function nextTick(fn) {
	add_render_callback(fn);
	schedule_update();
	return update_promise;
}

export function add_binding_callback(fn) {
	binding_callbacks.push(fn);
}

export function flush() {
	const seen_callbacks = new Set();

	do {
		// first, call beforeUpdate functions
		// and update components
		while (dirty_components.length) {
			const component = dirty_components.shift();
			set_current_component(component);
			update(component.$$);
		}

		while (binding_callbacks.length) binding_callbacks.shift()();

		// then, once components are updated, call
		// afterUpdate functions. This may cause
		// subsequent updates...
		while (render_callbacks.length) {
			const callback = render_callbacks.pop();
			if (!seen_callbacks.has(callback)) {
				callback();

				// ...so guard against infinite loops
				seen_callbacks.add(callback);
			}
		}
	} while (dirty_components.length);

	update_promise = null;
}

function update($$) {
	if ($$.fragment) {
		$$.update($$.dirty);
		run_all($$.before_render);
		$$.fragment.p($$.dirty, $$.ctx);
		$$.dirty = null;

		$$.after_render.forEach(add_render_callback);
	}
}<commit_msg>make callback optional<commit_after>import { run_all } from './utils.js';
import { set_current_component } from './lifecycle.js';

export let dirty_components = [];
export const intros = { enabled: false };

let update_promise;
const binding_callbacks = [];
const render_callbacks = [];

export function schedule_update() {
	if (!update_promise) {
		update_promise = Promise.resolve().then(flush);
	}
}

export function add_render_callback(fn) {
	render_callbacks.push(fn);
}

export function nextTick(fn) {
	if (fn) add_render_callback(fn);
	schedule_update();
	return update_promise;
}

export function add_binding_callback(fn) {
	binding_callbacks.push(fn);
}

export function flush() {
	const seen_callbacks = new Set();

	do {
		// first, call beforeUpdate functions
		// and update components
		while (dirty_components.length) {
			const component = dirty_components.shift();
			set_current_component(component);
			update(component.$$);
		}

		while (binding_callbacks.length) binding_callbacks.shift()();

		// then, once components are updated, call
		// afterUpdate functions. This may cause
		// subsequent updates...
		while (render_callbacks.length) {
			const callback = render_callbacks.pop();
			if (!seen_callbacks.has(callback)) {
				callback();

				// ...so guard against infinite loops
				seen_callbacks.add(callback);
			}
		}
	} while (dirty_components.length);

	update_promise = null;
}

function update($$) {
	if ($$.fragment) {
		$$.update($$.dirty);
		run_all($$.before_render);
		$$.fragment.p($$.dirty, $$.ctx);
		$$.dirty = null;

		$$.after_render.forEach(add_render_callback);
	}
}<|endoftext|>


<commit_before>

IONUX.Models.EditableResource = Backbone.Model.extend({
  idAttribute: '_id',
  schema: function(){
    return this.make_schema();
  },
  initialize: function(){},
  url: function(){
    return window.location.pathname.replace(/edit$/,'');
  },
  make_schema: function(){
    var self = this;
    var schema = {};
    _.each(this.attributes, function(value, key){
      if (!_.isObject(value) && !(key=='ts_updated' || key=='ts_created')){
        console.log(key, value, typeof value);
        switch(typeof(value)){
          case 'boolean':
            schema[key] = 'Checkbox';
            break;
          case 'number':
            schema[key] = 'Number';
            break;
          default:
            schema[key] = 'Text'
        };
      };
    });
    return schema;
  },
});

IONUX.Models.Search = Backbone.Model.extend();

IONUX.Models.Search = Backbone.Model.extend({
    url: function(){
        return "/search/?query="+this.get("search_query");
    },
    parse: function(resp){
        make_iso_timestamps(resp);
        return resp.data;
    }
});

// For use with collections of Resource Types, i.e. InstrumentDevice, PlatformDevice, etc.
IONUX.Models.Session = Backbone.Model.extend({
    defaults: {
        actor_id: null,
        user_id: null,
        name: "Guest",
        is_logged_in: false,
        is_registered: null,
        version: {},
        roles: [],
        ui_mode: 'PRODUCTION',
        is_polling: false,
        ui_theme_dark: false
    },
    url: '/session/',
    initialize: function() {
      _.bindAll(this);
    },
    parse: function(resp){
        return resp.data;
    },
    is_logged_in: function(){
      return this.get('is_logged_in');
    },
    is_registered: function() {
      return this.get('is_registered');
    },
    is_resource_owner: function(){
      return _.findWhere(MODEL_DATA.owners, {_id: this.get('user_id')}) ? true : false;
    },
    set_polling: function() {
      if (this.get('is_logged_in')) {
        this.set('is_polling', true);
        setTimeout(this.poll, 60000);
      };
    },
    poll: function() {
      var self = this;
      var existing_roles = _.clone(this.get('roles'));
      if (this.get('is_polling')) {
        this.fetch({
          global: false,
          success: function(resp) {
            self.set_polling();
            if (!_.isEqual(existing_roles, resp.get('roles'))) {
              self.check_roles(existing_roles);
            };
          },
          error: function(resp) {
            self.set('is_polling', false);
          }
        });
      };
    },
    check_roles: function(existing_roles) {
      var roles = this.get('roles');
      var new_roles = {};
      _.each(roles, function(v,k) {
        if (!_.has(existing_roles, k)) {
          new_roles[k] = v;
        } else {
          var added_roles = _.difference(roles[k], existing_roles[k]);
          if (added_roles.length) new_roles[k] = added_roles;
        };
      });
      if (!_.isEmpty(new_roles)) new IONUX.Views.NewRoles({new_roles: new_roles}).render().el;
    },
});

IONUX.Models.Resource = Backbone.Model.extend({
  initialize: function(options) {
    this.resource_id = options.resource_id
  },
  url: function() {
    return '/resource/read/'+this.resource_id+'/';
  },
  parse: function(resp) {
    return resp.data;
  },
});

IONUX.Models.EventType = Backbone.Model.extend({
    url: '/event_types/',
    parse: function(resp){
        return resp.data;
    },
});

IONUX.Models.Layout = Backbone.Model.extend({
  url: '/layout',
  parse: function(resp){
    return resp.data;
  },
});

// Timestamp conversion methods to call when parsing response.
// Maybe put these into IONUX.Helpers namespace?
var epoch_to_iso = function(epoch_time){
    try {
      return new Date(parseInt(epoch_time)).toISOString().replace(/T/, ' ').replace(/.\d{3}Z$/, 'Z');
    } catch (err) {
      return epoch_time;
    };
};

var make_iso_timestamps = function(resp) {
  _.each(resp, function(val, key) {
      if (key == 'ts_created' || key == 'ts_updated'){
        resp[key] = epoch_to_iso(val);
      };
      if (typeof val == 'object') {
        make_iso_timestamps(val);
      };
  });
  return;
};

IONUX.Models.ResourceExtension = Backbone.Model.extend({
    url: function(){
        return '/'+this.get('resource_type')+'/extension/'+this.get('resource_id')+'/';
    },
    parse: function(resp){
        make_iso_timestamps(resp);
        return resp.data;
    }
});

IONUX.Models.Observatory = Backbone.Model.extend({
  url: "/observatories/",
  idAttribute: "_id",
});

IONUX.Models.ObservatoryFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/observatories/"+this.get("observatory_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.UserRequest = Backbone.Model.extend({});

IONUX.Models.Platform = Backbone.Model.extend({
  url: "/platforms/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.PlatformFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/platforms/"+this.get("platform_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.PlatformModel = Backbone.Model.extend({
  url: '/platform_models/',
  idAttribute: '_id',

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.PlatformModelFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/platform_models/'+this.get('platform_model_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.Instrument = Backbone.Model.extend({
  url: "/instruments/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

// IONUX.Models.Instrument = Backbone.Model.extend({
//   url: "/instrument_models/",
//   idAttribute: "_id"
// });

IONUX.Models.InstrumentFacepageModelLegacy = Backbone.Model.extend({
  url: function(){
    return "/instruments/"+this.get("instrument_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.InstrumentModel = Backbone.Model.extend({
  url: '/instrument_models/',
  idAttribute: '_id',

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.InstrumentModelFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/instrument_models/'+this.get('instrument_model_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.InstrumentAgent = Backbone.Model.extend({
  url: '/instrument_agents/',
  idAttribute: '_id',

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.InstrumentAgentFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/instrument_agents/'+this.get('instrument_agent_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.DataProcessDefinition = Backbone.Model.extend({
  url: "/data_process_definitions/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.DataProcessDefinitionFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/data_process_definitions/"+this.get("data_process_definition_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.DataProduct = Backbone.Model.extend({
  url: "/data_products/",
  idAttribute: "_id",

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.DataProductFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/data_products/"+this.get("data_product_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.FrameOfReference = Backbone.Model.extend({
  url: "/frame_of_references/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.FrameOfReferenceFacepage = Backbone.Model.extend({
  url: function() {
    return "/frame_of_references/"+this.get("frame_of_reference_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.UserRegistrationModel = IONUX.Models.EditableResource.extend({
  url: "/userprofile/",
  idAttribute: "_id",

  schema: {
    'contact': {
      type: 'Object',
      title: false,
      subSchema: {
        individual_names_given: { type: 'Text', title: 'Given Name(s)' },
        individual_name_family: { type: 'Text', title: 'Family Name' },
        organization_name:      { type: 'Text', title: 'Organization Name' },
        position_name:          { type: 'Text', title: 'Position' },
        email:                  { type: 'Text', title: 'Email', dataType: 'email', validators: ['required', 'email']},
        street_address:         { type: 'Text', title: 'Street Address' },
        city:                   { type: 'Text', title: 'City' },
        postal_code:            { type: 'Text', title: 'Postal Code' },
        country:                { type: 'Text', title: 'Country' },
        url:                    { type: 'Text', title: 'Contact URL' },
        phones:                 { type: 'List', itemType: 'Phone' },
      },
    },

    // 'variables': {
    //   type: 'Object',
    //   title: false,
    //   subSchema: {
    //     notification_contact:   { type: 'Radio',    title: 'Notification Method', options: ['Email', 'SMS'] },
    //     sms_email:              { type: 'Checkbox', title: "Use email->SMS" },
    //     ooi_system_change:      { type: 'Checkbox', title: "OOI system change information" },
    //     ooi_project_updates:    { type: 'Checkbox', title: "OOI project updates" },
    //     ocean_leadership_news:  { type: 'Checkbox', title: "Ocean Leadership News" },
    //     ux_improvement_program: { type: 'Checkbox', title: "Participate in OOI User Experience Improvement Program" },
    //   }
    // },

  },

  attr_convert: {
    'name': {
      parse: function(v) {
        /**
         * Input: value of name field.
         * Ouptut: the same. Not presented on form.
         */
        return v;
      },
      serialize: function(v) {
        /**
         * Input: whatever value we parsed initially. May be empty.
         * Output: the concatentation of contact.individual_names_given + individual_name_family
         */
        var contact = this.get('contact');
        return contact.individual_names_given + " " + contact.individual_name_family;
      },
    },
    'variables': {
      parse: function(v) {
        /**
         * Input: array of { name: <key>, value: <value> } literal objects
         * Output: object with keys -> values
         */
        return _.object(_.map(v, function(vv) { return [vv.name, vv.value]; } ));
      },
      serialize: function(v) {
        /**
         * Input: object with keys -> values
         * Output: array of { name: <key>, value: <value> } literal objects
         */
        return _.map(v, function(vv, kk) { return { name: kk, value: vv }; });
      },
    },
  },

  attr_convert_serialize: function(attrs) {
    /**
     * Call this from your toJSON.
     */
    attrs = _.clone(attrs);
    var self = this;
    _.each(this.attr_convert, function(v, k) {
      if (_.has(attrs, k)) {
        attrs[k] = v.serialize.call(self, attrs[k]);
      }
    });

    return attrs;
  },

  attr_convert_parse: function(data) {
    /**
     * Call this from your parse method.
     */
    data = _.clone(data);
    var self = this;
    _.each(this.attr_convert, function(v, k) {
      if (_.has(data, k)) {
        data[k] = v.parse.call(self, data[k]);
      }
    });

    return data;
  },

  toJSON: function() {
    var o = IONUX.Models.EditableResource.prototype.toJSON.call(this);
    o = this.attr_convert_serialize(o);

    o['contact']['type_'] = 'ContactInformation';

    return o;
  },

  parse: function(resp) {
    var data = resp.data;
    if (data != null)
      data = this.attr_convert_parse(data);
    return data;
  },

  merge: function(attribute, value) {
    /**
     * Instead of overwriting full objects, this method merges the passed in value
     * on top of the current value.
     */

    var curval = _.clone(this.get(attribute) || {});
    _.extend(curval, value);
    this.set(attribute, curval);

    return curval;
  },
});

IONUX.Models.User = Backbone.Model.extend({
  url: "/users/",
  idAttribute: "_id"
});

IONUX.Models.UserFacepageModel = Backbone.Model.extend({
  url: function() {
    return "/users/"+this.get("user_id")+"/";
  },
  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.ResourceType = Backbone.Model.extend({})
<commit_msg>Fixes time parsing<commit_after>

IONUX.Models.EditableResource = Backbone.Model.extend({
  idAttribute: '_id',
  schema: function(){
    return this.make_schema();
  },
  initialize: function(){},
  url: function(){
    return window.location.pathname.replace(/edit$/,'');
  },
  make_schema: function(){
    var self = this;
    var schema = {};
    _.each(this.attributes, function(value, key){
      if (!_.isObject(value) && !(key=='ts_updated' || key=='ts_created')){
        console.log(key, value, typeof value);
        switch(typeof(value)){
          case 'boolean':
            schema[key] = 'Checkbox';
            break;
          case 'number':
            schema[key] = 'Number';
            break;
          default:
            schema[key] = 'Text'
        };
      };
    });
    return schema;
  },
});

IONUX.Models.Search = Backbone.Model.extend();

IONUX.Models.Search = Backbone.Model.extend({
    url: function(){
        return "/search/?query="+this.get("search_query");
    },
    parse: function(resp){
        make_iso_timestamps(resp);
        return resp.data;
    }
});

// For use with collections of Resource Types, i.e. InstrumentDevice, PlatformDevice, etc.
IONUX.Models.Session = Backbone.Model.extend({
    defaults: {
        actor_id: null,
        user_id: null,
        name: "Guest",
        is_logged_in: false,
        is_registered: null,
        version: {},
        roles: [],
        ui_mode: 'PRODUCTION',
        is_polling: false,
        ui_theme_dark: false
    },
    url: '/session/',
    initialize: function() {
      _.bindAll(this);
    },
    parse: function(resp){
        return resp.data;
    },
    is_logged_in: function(){
      return this.get('is_logged_in');
    },
    is_registered: function() {
      return this.get('is_registered');
    },
    is_resource_owner: function(){
      return _.findWhere(MODEL_DATA.owners, {_id: this.get('user_id')}) ? true : false;
    },
    set_polling: function() {
      if (this.get('is_logged_in')) {
        this.set('is_polling', true);
        setTimeout(this.poll, 60000);
      };
    },
    poll: function() {
      var self = this;
      var existing_roles = _.clone(this.get('roles'));
      if (this.get('is_polling')) {
        this.fetch({
          global: false,
          success: function(resp) {
            self.set_polling();
            if (!_.isEqual(existing_roles, resp.get('roles'))) {
              self.check_roles(existing_roles);
            };
          },
          error: function(resp) {
            self.set('is_polling', false);
          }
        });
      };
    },
    check_roles: function(existing_roles) {
      var roles = this.get('roles');
      var new_roles = {};
      _.each(roles, function(v,k) {
        if (!_.has(existing_roles, k)) {
          new_roles[k] = v;
        } else {
          var added_roles = _.difference(roles[k], existing_roles[k]);
          if (added_roles.length) new_roles[k] = added_roles;
        };
      });
      if (!_.isEmpty(new_roles)) new IONUX.Views.NewRoles({new_roles: new_roles}).render().el;
    },
});

IONUX.Models.Resource = Backbone.Model.extend({
  initialize: function(options) {
    this.resource_id = options.resource_id
  },
  url: function() {
    return '/resource/read/'+this.resource_id+'/';
  },
  parse: function(resp) {
    return resp.data;
  },
});

IONUX.Models.EventType = Backbone.Model.extend({
    url: '/event_types/',
    parse: function(resp){
        return resp.data;
    },
});

IONUX.Models.Layout = Backbone.Model.extend({
  url: '/layout',
  parse: function(resp){
    return resp.data;
  },
});

// Timestamp conversion methods to call when parsing response.
// Maybe put these into IONUX.Helpers namespace?
var epoch_to_iso = function(epoch_time){
    try {
      return new Date(parseInt(epoch_time)).toISOString().replace(/T/, ' ').replace(/.\d{3}Z$/, 'Z');
    } catch (err) {
      return epoch_time;
    };
};

var make_iso_timestamps = function(resp) {
  if(resp != null && 'type_' in resp) {
    if('ts_created' in resp) {
      resp['ts_created'] = epoch_to_iso(resp['ts_created']);
    }
    if('ts_updated' in resp) {
      resp['ts_updated'] = epoch_to_iso(resp['ts_updated']);
    }
  }

  _.each(resp, function(val, key) {
      if (typeof val == 'object') {
        make_iso_timestamps(val);
      };
  });
  return;
};

IONUX.Models.ResourceExtension = Backbone.Model.extend({
    url: function(){
        return '/'+this.get('resource_type')+'/extension/'+this.get('resource_id')+'/';
    },
    parse: function(resp){
        make_iso_timestamps(resp);
        return resp.data;
    }
});

IONUX.Models.Observatory = Backbone.Model.extend({
  url: "/observatories/",
  idAttribute: "_id",
});

IONUX.Models.ObservatoryFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/observatories/"+this.get("observatory_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.UserRequest = Backbone.Model.extend({});

IONUX.Models.Platform = Backbone.Model.extend({
  url: "/platforms/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.PlatformFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/platforms/"+this.get("platform_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.PlatformModel = Backbone.Model.extend({
  url: '/platform_models/',
  idAttribute: '_id',

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.PlatformModelFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/platform_models/'+this.get('platform_model_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.Instrument = Backbone.Model.extend({
  url: "/instruments/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

// IONUX.Models.Instrument = Backbone.Model.extend({
//   url: "/instrument_models/",
//   idAttribute: "_id"
// });

IONUX.Models.InstrumentFacepageModelLegacy = Backbone.Model.extend({
  url: function(){
    return "/instruments/"+this.get("instrument_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.InstrumentModel = Backbone.Model.extend({
  url: '/instrument_models/',
  idAttribute: '_id',

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.InstrumentModelFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/instrument_models/'+this.get('instrument_model_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.InstrumentAgent = Backbone.Model.extend({
  url: '/instrument_agents/',
  idAttribute: '_id',

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.InstrumentAgentFacepageModel = Backbone.Model.extend({
  url: function() {
    return '/instrument_agents/'+this.get('instrument_agent_id')+'/';
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.DataProcessDefinition = Backbone.Model.extend({
  url: "/data_process_definitions/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.DataProcessDefinitionFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/data_process_definitions/"+this.get("data_process_definition_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.DataProduct = Backbone.Model.extend({
  url: "/data_products/",
  idAttribute: "_id",

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp;
  }
});

IONUX.Models.DataProductFacepageModel = Backbone.Model.extend({
  url: function(){
    return "/data_products/"+this.get("data_product_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.FrameOfReference = Backbone.Model.extend({
  url: "/frame_of_references/",
  idAttribute: "_id",

  parse: function(resp){
    make_iso_timestamps(resp);
    return resp;
  },
});

IONUX.Models.FrameOfReferenceFacepage = Backbone.Model.extend({
  url: function() {
    return "/frame_of_references/"+this.get("frame_of_reference_id")+"/";
  },

  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.UserRegistrationModel = IONUX.Models.EditableResource.extend({
  url: "/userprofile/",
  idAttribute: "_id",

  schema: {
    'contact': {
      type: 'Object',
      title: false,
      subSchema: {
        individual_names_given: { type: 'Text', title: 'Given Name(s)' },
        individual_name_family: { type: 'Text', title: 'Family Name' },
        organization_name:      { type: 'Text', title: 'Organization Name' },
        position_name:          { type: 'Text', title: 'Position' },
        email:                  { type: 'Text', title: 'Email', dataType: 'email', validators: ['required', 'email']},
        street_address:         { type: 'Text', title: 'Street Address' },
        city:                   { type: 'Text', title: 'City' },
        postal_code:            { type: 'Text', title: 'Postal Code' },
        country:                { type: 'Text', title: 'Country' },
        url:                    { type: 'Text', title: 'Contact URL' },
        phones:                 { type: 'List', itemType: 'Phone' },
      },
    },

    // 'variables': {
    //   type: 'Object',
    //   title: false,
    //   subSchema: {
    //     notification_contact:   { type: 'Radio',    title: 'Notification Method', options: ['Email', 'SMS'] },
    //     sms_email:              { type: 'Checkbox', title: "Use email->SMS" },
    //     ooi_system_change:      { type: 'Checkbox', title: "OOI system change information" },
    //     ooi_project_updates:    { type: 'Checkbox', title: "OOI project updates" },
    //     ocean_leadership_news:  { type: 'Checkbox', title: "Ocean Leadership News" },
    //     ux_improvement_program: { type: 'Checkbox', title: "Participate in OOI User Experience Improvement Program" },
    //   }
    // },

  },

  attr_convert: {
    'name': {
      parse: function(v) {
        /**
         * Input: value of name field.
         * Ouptut: the same. Not presented on form.
         */
        return v;
      },
      serialize: function(v) {
        /**
         * Input: whatever value we parsed initially. May be empty.
         * Output: the concatentation of contact.individual_names_given + individual_name_family
         */
        var contact = this.get('contact');
        return contact.individual_names_given + " " + contact.individual_name_family;
      },
    },
    'variables': {
      parse: function(v) {
        /**
         * Input: array of { name: <key>, value: <value> } literal objects
         * Output: object with keys -> values
         */
        return _.object(_.map(v, function(vv) { return [vv.name, vv.value]; } ));
      },
      serialize: function(v) {
        /**
         * Input: object with keys -> values
         * Output: array of { name: <key>, value: <value> } literal objects
         */
        return _.map(v, function(vv, kk) { return { name: kk, value: vv }; });
      },
    },
  },

  attr_convert_serialize: function(attrs) {
    /**
     * Call this from your toJSON.
     */
    attrs = _.clone(attrs);
    var self = this;
    _.each(this.attr_convert, function(v, k) {
      if (_.has(attrs, k)) {
        attrs[k] = v.serialize.call(self, attrs[k]);
      }
    });

    return attrs;
  },

  attr_convert_parse: function(data) {
    /**
     * Call this from your parse method.
     */
    data = _.clone(data);
    var self = this;
    _.each(this.attr_convert, function(v, k) {
      if (_.has(data, k)) {
        data[k] = v.parse.call(self, data[k]);
      }
    });

    return data;
  },

  toJSON: function() {
    var o = IONUX.Models.EditableResource.prototype.toJSON.call(this);
    o = this.attr_convert_serialize(o);

    o['contact']['type_'] = 'ContactInformation';

    return o;
  },

  parse: function(resp) {
    var data = resp.data;
    if (data != null)
      data = this.attr_convert_parse(data);
    return data;
  },

  merge: function(attribute, value) {
    /**
     * Instead of overwriting full objects, this method merges the passed in value
     * on top of the current value.
     */

    var curval = _.clone(this.get(attribute) || {});
    _.extend(curval, value);
    this.set(attribute, curval);

    return curval;
  },
});

IONUX.Models.User = Backbone.Model.extend({
  url: "/users/",
  idAttribute: "_id"
});

IONUX.Models.UserFacepageModel = Backbone.Model.extend({
  url: function() {
    return "/users/"+this.get("user_id")+"/";
  },
  parse: function(resp) {
    make_iso_timestamps(resp);
    return resp.data;
  }
});

IONUX.Models.ResourceType = Backbone.Model.extend({})
<|endoftext|>


<commit_before>// Game implementation

var Target = function(targetValue) {

    this.targetValue = targetValue;
    this.currentValue = 0;
    this.includedParts = [];
    this.posx = 250;
    this.posy = 250;
    this.radius = 100;
    this.partIds =[];

    this.includePart = function(part) {
        this.currentValue += part.value;
        this.includedParts.push(part);
        this.partIds.push(part.id);
        console.log(this.includedParts)
        console.log(this.partIds)
    };

    this.excludePart = function(part) {
        this.currentValue -= part.value;
        var index = this.partIds.indexOf(part.id);
        if (index > -1) {
            this.partIds.splice(index, 1);
            this.includedParts.splice(index, 1);

        };
        console.log(this.includedParts);
        console.log(this.partIds);
    };

    this.draw = function() {
        context.beginPath();
        context.arc(this.posx, this.posy, this.radius,0,2*Math.PI);
        context.stroke();
        context.font="40px Georgia";
        context.fillText(this.targetValue, this.posx - 30, this.posy - 10);
        context.font="40px Georgia";
        context.fillText(this.currentValue, this.posx - 30, this.posy + 20);
    };
};

var idCounter = 0;

var Part = function(value) {
    this.value = value;
    this.posx = 50+ Math.random()*300;
    this.posy = 100;
    this.radius = 30;
    this.id = idCounter++;
    console.log(idCounter);

    this.draw = function() {

        context.beginPath();
        context.arc(this.posx, this.posy, this.radius,0,2*Math.PI);
        context.stroke();
        context.font="40px Georgia";
        context.fillText(this.value, this.posx - 20, this.posy + 10);
    };
};

var GameSession = function() {

    var theCanvas = document.getElementById("myCanvas");
    var bRect = theCanvas.getBoundingClientRect();
    var number_of_parts;
    var parts;
    var target;
    var context = theCanvas.getContext("2d");
    var dragging;
    var dragIndex;
    var dragHoldX;
    var dragHoldY;

    this.init = function() {

        //later in make target function
        target = new Target(100);
        parts = [];

        // Later in make parts function
        var part1 = new Part(50);
        var part2 = new Part(50);

        parts.push(part1);
        parts.push(part2);

        target.draw();

    };
    function drawParts() {
        for (i = 0; i < parts.length; i++) {
            parts[i].draw();
        };
    };

    this.newGame = function() {
        this.reset();
        target.draw();
        drawParts();

        theCanvas.addEventListener("mousedown", mouseDownListener, false);
    };

    function mouseDownListener(evt) {
        var i;
        //We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
        //only the topmost one will be dragged.

        // THIS NEEDS TO CHANGE
        var highestIndex = -1;

        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);
        dragging = false;
        console.log(dragging)
        for (i=0; i < parts.length; i++) {
            console.log(parts)
            if  (hitTest(parts[i], mouseX, mouseY)) {
                dragging = true;
		console.log(dragging);
                if (i > highestIndex) {
                    //We will pay attention to the point on the object where the mouse is "holding" the object:
                    dragHoldX = mouseX - parts[i].posx;
                    dragHoldY = mouseY - parts[i].posy;
                    highestIndex = i;
                    dragIndex = i;
                }
            }
        }

        if (dragging) {
            window.addEventListener("mousemove", mouseMoveListener, false);
        }

        theCanvas.removeEventListener("mousedown", mouseDownListener, false);
        window.addEventListener("mouseup", mouseUpListener, false);
    }

    function mouseUpListener(evt) {
        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);

        theCanvas.addEventListener("mousedown", mouseDownListener, false);
        window.removeEventListener("mouseup", mouseUpListener, false);

        if (hitTest(target, mouseX, mouseY) && (target.partIds.indexOf(parts[dragIndex].id) < 0)) {
            target.includePart(parts[dragIndex]);
            context.clearRect(0, 0, b.width, b.height);
            context.restore();
            target.draw();
            drawParts();

        } else if ( dragging === true && hitTest(target, mouseX,mouseY) === false && (target.partIds.indexOf(parts[dragIndex].id) > -1)) {
            target.excludePart(parts[dragIndex]);
            context.clearRect(0, 0, b.width, b.height);
            context.restore();
            target.draw();
            drawParts();
        };

        if (dragging) {
            dragging = false;
            window.removeEventListener("mousemove", mouseMoveListener, false);
        };

    }

    function hitTest(shape,mx,my) {

        var dx;
        var dy;
        dx = mx - shape.posx;
        dy = my - shape.posy;

        //a "hit" will be registered if the distance away from the center is less than the radius of the circular object
        return (dx*dx + dy*dy < shape.radius*shape.radius);
    };

    function mouseMoveListener(evt) {
        var posX;
        var posY;
        var partRad = parts[dragIndex].radius;
        var minX = partRad;
        var maxX = theCanvas.width - partRad;
        var minY = partRad;
        var maxY = theCanvas.height - partRad;
        //getting mouse position correctly
        var bRect = theCanvas.getBoundingClientRect();
        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);

        //clamp x and y positions to prevent object from dragging outside of canvas
        posX = mouseX - dragHoldX;
        posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
        posY = mouseY - dragHoldY;
        posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);

        parts[dragIndex].posx = posX;
        parts[dragIndex].posy = posY;

        context.clearRect(0, 0, b.width, b.height);
        context.restore();
        target.draw();
        drawParts();

    };

    this.update = function() {
        // Draw all parts

        // Check if click is inside target and recalculate

        // Check if target is completed

    };

    this.reset = function() {
        this.init();
        context.clearRect(0, 0, b.width, b.height);
        context.restore();

        this.parts = [];
    };

    this.complete = function() {

    };

};
var b = document.getElementById("myCanvas");
var context = b.getContext("2d");

gamesession = new GameSession()

document.getElementById("body").onload = function() {gamesession.init()};

<commit_msg>dynamic levels<commit_after>// Game implementation

var Target = function(targetValue) {
    this.targetValue = targetValue;
    this.currentValue = 0;
    this.includedParts = [];
    this.posx = 250;
    this.posy = 250;
    this.radius = 100;
    this.partIds =[];

    this.includePart = function(part, sign) {
        if (sign === "add") {
            this.currentValue += Math.abs(part.value);
        } else if(sign === "sub") {
            this.currentValue -= Math.abs(part.value);
        }
        this.includedParts.push([part, sign]);
        this.partIds.push(part.id);
    };

    this.excludePart = function(part) {
        var index = this.partIds.indexOf(part.id);
        sign = this.includedParts[index][1]
        if (sign === "add") {
            this.currentValue -= Math.abs(part.value);
        } else if(sign === "sub") {
            this.currentValue += Math.abs(part.value);
        }
        if (index > -1) {
            this.partIds.splice(index, 1);
            this.includedParts.splice(index, 1);

        };
    };

    this.draw = function() {
        context.beginPath();
        context.arc(this.posx, this.posy, this.radius,0,2*Math.PI);
        context.stroke();
        context.font="40px Georgia";
        context.fillText(this.targetValue, this.posx - 30, this.posy - 10);
        context.font="40px Georgia";
        context.fillText(this.currentValue, this.posx - 30, this.posy + 20);
    };
};

var idCounter = 0;

var Part = function(value) {
    this.value = value;
    this.posx = 50+ Math.random()*300;
    this.posy = 100;
    this.radius = 30;
    this.id = idCounter++;

    this.draw = function() {

        context.beginPath();
        context.arc(this.posx, this.posy, this.radius,0,2*Math.PI);
        context.stroke();
        context.font="20px Georgia";
        context.fillText(Math.abs(this.value), this.posx - 20, this.posy + 10);
    };
};

var GameSession = function() {

    var theCanvas = document.getElementById("myCanvas");
    var bRect = theCanvas.getBoundingClientRect();
    var number_of_parts;
    var parts;
    var target;
    var context = theCanvas.getContext("2d");
    var dragging;
    var dragIndex;
    var dragHoldX;
    var dragHoldY;
    var add = false;
    var sub = false;
    this.addition = function () {
        if(add === false){
             sub = false;
             add = true;
             document.getElementById("additionButton").style.background="rgba(255,0,0,0.6)"
             document.getElementById("subtractionButton").style.background="#D3D3D3"
        } else if (add === true){
             add = false;
             document.getElementById("additionButton").style.background="#D3D3D3"
        }
    }
    this.subtraction = function () {
        if(sub === false){
             add = false;
             sub = true;
             document.getElementById("subtractionButton").style.background="rgba(255,0,0,0.6)"
             document.getElementById("additionButton").style.background="#D3D3D3"
        } else if (sub === true){
             sub = false;
             document.getElementById("subtractionButton").style.background="#D3D3D3"

        }
    }
    this.get_sign = function () {
        if (add === true) {
           return "add"
        } else if (sub === true) {
           return "sub"
        }
    }

    this.init = function() {

        //later in make target function
        target = new Target(0);
        parts = [];
        level = 4;
        var sum = target.targetValue;
        for (var i=0;i<4;i++) {
           var partValue = Math.floor((Math.random() - 0.5) * 200)
           sum += partValue;
           parts[i] = new Part(partValue);
        }
        var lastPartValue = target.targetValue - sum;
        //var part1Value = Math.floor((Math.random() - 0.5) * 200);
        //var part2Value = Math.floor((Math.random() - 0.5) * 200);
        //var part3Value = target.targetValue - part1Value - part2Value;
        //var part1 = new Part(part1Value);
        //var part2 = new Part(part2Value);
        //var part3 = new Part(part3Value);
        //parts.push(part1);
        //parts.push(part2);
        //parts.push(part3);
        // Later in make parts function

        target.draw();

    };
    function drawParts() {
        for (i = 0; i < parts.length; i++) {
            parts[i].draw();
        };
    };

    this.newGame = function() {
        this.reset();
        target.draw();
        drawParts();

        theCanvas.addEventListener("mousedown", mouseDownListener, false);
    };

    function mouseDownListener(evt) {
        var i;
        //We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
        //only the topmost one will be dragged.

        // THIS NEEDS TO CHANGE
        var highestIndex = -1;

        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);
        dragging = false;
        for (i=0; i < parts.length; i++) {
            if  (hitTest(parts[i], mouseX, mouseY)) {
                dragging = true;
                if (i > highestIndex) {
                    //We will pay attention to the point on the object where the mouse is "holding" the object:
                    dragHoldX = mouseX - parts[i].posx;
                    dragHoldY = mouseY - parts[i].posy;
                    highestIndex = i;
                    dragIndex = i;
                }
            }
        }

        if (dragging) {
            window.addEventListener("mousemove", mouseMoveListener, false);
        }

        theCanvas.removeEventListener("mousedown", mouseDownListener, false);
        window.addEventListener("mouseup", mouseUpListener, false);
    }

    function mouseUpListener(evt) {
        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);

        theCanvas.addEventListener("mousedown", mouseDownListener, false);
        window.removeEventListener("mouseup", mouseUpListener, false);

        if (hitTest(target, mouseX, mouseY) && (target.partIds.indexOf(parts[dragIndex].id) < 0)) {
            target.includePart(parts[dragIndex], gamesession.get_sign());
            context.clearRect(0, 0, b.width, b.height);
            context.restore();
            target.draw();
            drawParts();

        } else if ( dragging === true && hitTest(target, mouseX,mouseY) === false && (target.partIds.indexOf(parts[dragIndex].id) > -1)) {
            target.excludePart(parts[dragIndex]);
            context.clearRect(0, 0, b.width, b.height);
            context.restore();
            target.draw();
            drawParts();
        };

        if (dragging) {
            dragging = false;
            window.removeEventListener("mousemove", mouseMoveListener, false);
        };
        completed();
    };

    function hitTest(shape,mx,my) {

        var dx;
        var dy;
        dx = mx - shape.posx;
        dy = my - shape.posy;

        //a "hit" will be registered if the distance away from the center is less than the radius of the circular object
        return (dx*dx + dy*dy < shape.radius*shape.radius);
    };

    function mouseMoveListener(evt) {
        var posX;
        var posY;
        var partRad = parts[dragIndex].radius;
        var minX = partRad;
        var maxX = theCanvas.width - partRad;
        var minY = partRad;
        var maxY = theCanvas.height - partRad;
        //getting mouse position correctly
        var bRect = theCanvas.getBoundingClientRect();
        mouseX = (evt.clientX - bRect.left)*(theCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top)*(theCanvas.height/bRect.height);

        //clamp x and y positions to prevent object from dragging outside of canvas
        posX = mouseX - dragHoldX;
        posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
        posY = mouseY - dragHoldY;
        posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);

        parts[dragIndex].posx = posX;
        parts[dragIndex].posy = posY;

        context.clearRect(0, 0, b.width, b.height);
        context.restore();
        target.draw();
        drawParts();

    };

    this.update = function() {
        // Draw all parts

        // Check if click is inside target and recalculate

        // Check if target is completed

    };

    this.reset = function() {
        idCounter = 0;
        this.init();
        context.clearRect(0, 0, b.width, b.height);
        context.restore();

        this.parts = [];
    };
    function completed() {
        if (target.includedParts.length === parts.length
            && target.currentValue === target.targetValue) {
           alert("You Got it")
        };

    };
};

var b = document.getElementById("myCanvas");
var context = b.getContext("2d");

gamesession = new GameSession()

document.getElementById("body").onload = function() {gamesession.init()};

<|endoftext|>