From e0ad0c1481387e73b1d2b00c0c35d7ef85db2dff Mon Sep 17 00:00:00 2001
From: PeterTheOne <petertheone@gmail.com>
Date: Fri, 29 Jan 2016 01:09:17 +0100
Subject: jingles: use Rivendell.Cart and Cut classes with Views, etc.

validate html, move rivendell.js init to apps.js, cleanup importer,
add update event to fetch functions instead of success callback.

diff --git a/www/index.html b/www/index.html
index 77bf69b..f2fce51 100644
--- a/www/index.html
+++ b/www/index.html
@@ -40,7 +40,7 @@
     <div id="loginbox">
 
       <form id="loginform" class="form-auth" role="form">
-        <img src="/img/helsinki.png" />
+        <img src="/img/helsinki.png" alt="radio helsinki logo" />
         <h1 class="form-auth-heading">Radio Helsinki - Import</h1>
         <input id="username" type="text" class="form-control" placeholder="Benutzername" required autofocus>
         <input id="password" type="password" class="form-control" placeholder="Passwort" required>
@@ -120,7 +120,10 @@
         <div class="row-fluid">
           <div class="span10">
             <form class="well form-inline">
-              <label class="control-label" for="show-selector"><h3>Sendung auswählen</h3></label>&nbsp;&nbsp;
+              <!-- todo: fix html error: h3 is not allowed in label -->
+              <label class="control-label" for="show-selector">
+                <h3>Sendung auswählen</h3>
+              </label>&nbsp;&nbsp;
               <select id="show-selector" class="main-selector" onchange="shows_showSelected()">
               </select>
             </form>
@@ -188,7 +191,10 @@
         <div class="alertbox"></div>
         <div class="row-fluid">
           <form class="well form-inline">
-            <label class="control-label" for="musicpool-selector"><h3>Musikpool auswählen</h3></label>&nbsp;&nbsp;
+            <!-- todo: fix html error: h3 is not allowed in label -->
+            <label class="control-label" for="musicpool-selector">
+              <h3>Musikpool auswählen</h3>
+            </label>&nbsp;&nbsp;
             <select id="musicpool-selector" class="main-selector">
             </select>
           </form>
diff --git a/www/js/apps.js b/www/js/apps.js
index 12aba8f..6bdf3d6 100644
--- a/www/js/apps.js
+++ b/www/js/apps.js
@@ -25,6 +25,7 @@
 var Rivendell = Rivendell || {};
 
 var apps_current = null;
+var rivendell = null;
 
 function apps_select(app) {
   $('.container').removeClass('fullWidth');
@@ -96,6 +97,10 @@ function apps_init() {
   apps_current = locationHrefValue();
 
   if(auth_token && auth_username) {
+    rivendell = new Rivendell.Rivendell(auth_username, auth_token, '/rd-bin/rdxport.cgi');
+    rivendell.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
+    rivendell.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
+
     apps_select(apps_current);
   }
 
@@ -121,5 +126,6 @@ function apps_cleanup() {
 
   $(window).off('popstate');
 
+  rivendell = null;
   apps_current = null;
 }
diff --git a/www/js/importer.js b/www/js/importer.js
index 5261f6d..8aa6c09 100644
--- a/www/js/importer.js
+++ b/www/js/importer.js
@@ -28,12 +28,18 @@ Rivendell.Importer = function() {
   this.$el = $('#uploadModal');
 };
 
-Rivendell.Importer.prototype.showUploadModal = function(group) {
+Rivendell.Importer.prototype.resetModal = function(group) {
   $('div.modal-header h3', this.$el).text("Datei auswählen...");
-  var form = $('<form>');
-  $('.modal-body', uploadModal).empty().append(form).css("background-image", "url('/img/dz-backdrop.png')");
+  $('.modal-body', this.$el).css("background-image", "url('/img/dz-backdrop.png')");
+  $('#uploadModal-confirm', this.$el).attr('disabled','disabled').off('click');
+};
+
+Rivendell.Importer.prototype.openModal = function(group) {
+  this.resetModal();
+
+  var $form = $('<form>');
   var self = this;
-  var dropzone = form.dropzone({
+  $form.dropzone({
     url: '/rd-bin/rdxport.cgi',
     parallelUploads: 1,
     maxFilesize: 2048,
@@ -44,37 +50,40 @@ Rivendell.Importer.prototype.showUploadModal = function(group) {
     acceptedFiles: '.flac,.wav,.ogg,.mp3',
     autoProcessQueue: false,
     init: function() {
-      this.on("addedfile", function(file) {
+      this.on('addedfile', function(file) {
         self.importFileAdded(this, file, group);
       });
-      this.on("error", function(file, msg) {
+      this.on('error', function(file, msg) {
         self.importFileSelectError(this, file, msg);
       });
     }
   });
 
+  $('.modal-body', this.$el).empty().append($form);
   this.$el.modal({keyboard: true});
 };
 
 Rivendell.Importer.prototype.importFileAdded = function(dropzone, file, group) {
-  $(dropzone.getAcceptedFiles()).each(function(idx, elem) { dropzone.removeFile(elem); });
+  // wtf? remove accepted files?
+  /*$(dropzone.getAcceptedFiles()).each(function(index, elem) {
+    dropzone.removeFile(elem);
+  });*/
 
   $('div.modal-body', this.$el).css("background-image", "url('/img/audio_file.png')");
   $('div.modal-header h3', this.$el).text(file.name);
+
   var self = this;
-  $('#uploadModal-confirm', this.$el).unbind('click').click(function() {
+  $('#uploadModal-confirm', this.$el).off('click').on('click', function() {
     self.importCartConfirm(dropzone, group);
   }).removeAttr('disabled');
 };
 
 Rivendell.Importer.prototype.importFileSelectError = function(dropzone, file, msg) {
-  $('div.modal-header h3', this.$el).text("Datei auswählen...");
-  $('div.modal-body', this.$el).css("background-image", "url('/img/dz-backdrop.png')");
-  $('#uploadModal-confirm', this.$el).attr('disabled','disabled').unbind('click');
+  this.resetModal();
   dropzone.removeFile(file);
 };
 
-Rivendell.Importer.prototype.importCartConfirm = function(dz, group) {
+Rivendell.Importer.prototype.importCartConfirm = function(dropzone, group) {
   this.$el.modal('hide');
 
   //var progressBar = group.addUpload();
@@ -84,25 +93,26 @@ Rivendell.Importer.prototype.importCartConfirm = function(dz, group) {
   //var cart_row = $('#show-cart-' + cart);
   //cart_row.find('.btn').attr('disabled','disabled');
   //var importing_row = jingles_newImportingEntry(cart);
-  //importing_row.find('button').unbind('click').click(function() { jingles_importCartCancel(cart, dz); });
+  //importing_row.find('button').unbind('click').click(function() { jingles_importCartCancel(cart, dropzone); });
   //cart_row.replaceWith(importing_row);
 
-  dz.off("error");
-  var files = dz.getAcceptedFiles();
+  var files = dropzone.getAcceptedFiles();
   //importing_row.find('.file-name').text(files[0].name);
   //jingles_importUpdateProgress(files[0], importing_row);
   var self = this;
-  dz.on('uploadprogress', function(file) { self.uploadProgress(file); });
-  dz.on('success', function(file) {
-    self.importFileUploadSuccess(dz, file, group);
+  dropzone.on('uploadprogress', function(file) {
+    self.uploadProgress(file);
+  });
+  dropzone.on('success', function(file) {
+    self.importFileUploadSuccess(dropzone, file, group);
   });
-  dz.on('error', function(file, msg, xhr) {
+  dropzone.off('error').on('error', function(file, msg, xhr) {
     self.importFileUploadError(this, file, msg, xhr);
   });
   /*$(files).each(function(index, file) {
-   self.importAddCut(dz, group, file);
+   self.importAddCut(dropzone, group, file);
    });*/
-  self.importAddCut(dz, group, files[0]);
+  self.importAddCut(dropzone, group, files[0]);
 };
 
 Rivendell.Importer.prototype.uploadProgress = function(file) {
@@ -128,31 +138,31 @@ Rivendell.Importer.prototype.uploadProgress = function(file) {
   }
 };
 
-Rivendell.Importer.prototype.importFileUploadSuccess = function(dz, file, group) {
+Rivendell.Importer.prototype.importFileUploadSuccess = function(dropzone, file, group) {
   //var command = { LOGIN_NAME: auth_username, PASSWORD: auth_token, DESCRIPTION: file. };
 
   //jingles_updateGroupCartInfo(cart);
-  dz.disable();
+  dropzone.disable();
   //progressBar.remove();
   group.fetchCarts();
   //$(this.currentGroup.mainCart).trigger('add');
 };
 
-Rivendell.Importer.prototype.importFileUploadError = function(dz, file, msg, xhr) {
+Rivendell.Importer.prototype.importFileUploadError = function(dropzone, file, msg, xhr) {
   //var error_row = jingles_newImportErrorEntry(cart, msg);
   //error_row.find('button').unbind('click').click(function() {
   //jingles_deleteCart(cart);
   //});
   //$('#show-cart-' + cart).replaceWith(error_row);
   //error_row.find('.file-name').text(file.name);
-  dz.disable();
+  dropzone.disable();
 };
 
-Rivendell.Importer.prototype.importAddCut = function(dz, group, file) {
+Rivendell.Importer.prototype.importAddCut = function(dropzone, group, file) {
   var cart = group.mainCart;
   rivendell.addAndEditCut(cart.number, {DESCRIPTION: file.name}, function(cutXml) {
     group.fetchCarts();
-    dz.on('sending', function(file, xhr, formData) {
+    dropzone.on('sending', function(file, xhr, formData) {
       var cutNumber = $(cutXml).find('cutNumber').text();
       var cutNumberLeading = cutNumber;
       switch (cutNumber.toString().length) {
@@ -174,8 +184,8 @@ Rivendell.Importer.prototype.importAddCut = function(dz, group, file) {
       formData.append('AUTOTRIM_LEVEL', cart.trimlevel);
       formData.append('USE_METADATA', 0); // don't set USE_METADATA 1 for jingles
     });
-    dz.processQueue();
+    dropzone.processQueue();
   }).fail(function(xhr, status, err) {
-    self.importFileUploadError(cart, dz, file, err, xhr);
+    self.importFileUploadError(cart, dropzone, file, err, xhr);
   });
 };
diff --git a/www/js/jingles.js b/www/js/jingles.js
index 65063bf..451f643 100644
--- a/www/js/jingles.js
+++ b/www/js/jingles.js
@@ -24,31 +24,40 @@
 
 var Rivendell = Rivendell || {};
 
-var rivendell = null;
 var importer = null;
-var groupList = null;
+var jingleGroupListView = null;
 
 function jingles_init() {
-  rivendell = new Rivendell.Rivendell(auth_username, auth_token, '/rd-bin/rdxport.cgi');
-  rivendell.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
-
   importer = new Rivendell.Importer();
 
-  groupList = new Rivendell.GroupList(rivendell);
-  // todo: move this elsewhere?
-  $('#app-jingles .groups').html('');
-  groupList.fetch('jingle');
+  var groupList = new Rivendell.GroupList(rivendell);
+  jingleGroupListView = new JingleGroupListView(groupList);
 }
 
 function jingles_cleanup() {
-  if (groupList) {
-    groupList.destroy();
-    groupList = null;
+  if (jingleGroupListView) {
+    jingleGroupListView.destroy();
+    jingleGroupListView = null;
   }
   importer = null;
-  rivendell = null;
 }
 
+var JingleGroupListView = function(model) {
+  this.model = model;
+
+  this.jingleGroupViews = [];
+
+  $('#app-jingles .groups').html('');
+
+  var self = this;
+  $(this.model).on('update', function() {
+    $(self.model.groups).each(function(index, group) {
+      self.jingleGroupViews.push(new JingleGroupView(group));
+    });
+  });
+  this.model.fetch('jingle');
+};
+
 var JingleGroup = function(groupName, description, lowcart, highcart, normlevel, trimlevel, title) {
   if (arguments.length = 1) {
     Rivendell.Group.call(this, groupName);
@@ -57,146 +66,126 @@ var JingleGroup = function(groupName, description, lowcart, highcart, normlevel,
     Rivendell.Group.call(this, groupName, description, lowcart, highcart, normlevel, trimlevel);
     this.title = title;
   }
+};
+JingleGroup.prototype = Object.create(Rivendell.Group.prototype);
+JingleGroup.prototype.constructor = JingleGroup;
 
-  this.mainCart = null;
-  this.deactivateCart = null;
+var JingleGroupView = function(model) {
+  this.model = model;
+
+  this.mainCartView = null;
+  this.deactivateCartView = null;
 
   this.$el = null;
 
   this.render();
-  this.fetchCarts();
-};
-JingleGroup.prototype = Object.create(Rivendell.Group.prototype);
-JingleGroup.prototype.constructor = JingleGroup;
 
-JingleGroup.prototype.fetchCarts = function() {
   var self = this;
-  $.when(
-    rivendell.listCart(this.lowcart, 1, function(cartXml) {
-      self.mainCart = self.createCartFromXml(cartXml, true);
-    }),
-    rivendell.listCart(this.highcart, 1, function(cartXml) {
-      self.deactivateCart = self.createCartFromXml(cartXml, false);
-    })
-  ).then(function() {
-      self.renderCarts();
-  });
-};
+  $(this.model).on('update', function() {
+    $('table > tbody', self.$el).html('');
 
-JingleGroup.prototype.createCartFromXml = function(cartXml, active) {
-  var cart = new JingleCart(
-    $(cartXml).find('number').text(),
-    $(cartXml).find('title').text(),
-    $(cartXml).find('groupName').text(),
-    this
-  );
+    self.model.mainCart = self.model.carts[0];
+    self.mainCartView = new JingleCartView(self.model.mainCart, self, true);
 
-  var self = this;
-  var cuts = $(cartXml).find("cutList").children();
-  cuts.each(function(index, cutXml) {
-    var cut = new JingleCut(
-      cart,
-      cart.number,
-      $(cutXml).find('cutName').text(),
-      $(cutXml).find('description').text(),
-      active
-    );
-
-    cart.addCut(cut);
+    self.model.deactivateCart = self.model.carts[1];
+    self.deactivateCartView = new JingleCartView(self.model.deactivateCart, self, false);
   });
 
-  return cart;
+  this.model.fetchCarts();
 };
 
-JingleGroup.prototype.render = function() {
+JingleGroupView.prototype.render = function() {
   var self = this;
 
-  this.$el = $('.jingleGroupTemplate').clone().removeClass('jingleGroupTemplate');
+  this.$el = $('#hiddenTemplates .jingleGroupTemplate').clone().removeClass('jingleGroupTemplate');
   this.$el.appendTo('#app-jingles .groups');
 
-  $('h2', this.$el).html(this.title);
+  $('h2', this.$el).html(this.model.title);
   $('table tbody tr', this.$el).remove();
 
   $('.uploadButton', this.$el).on('click', function() {
-    importer.showUploadModal(self);
+    importer.openModal(self.model);
   });
-
-  this.renderCarts();
 };
 
-JingleGroup.prototype.renderCarts = function() {
+JingleGroupView.prototype.destroy = function() {
   $('table > tbody', this.$el).html('');
+};
+
+var JingleCartView = function(model, groupView, active) {
+  this.model = model;
+  this.groupView = groupView;
+  this.active = active;
+
+  this.cutViews = [];
 
   var self = this;
-  if (this.mainCart) {
-    $.each(this.mainCart.cuts, function(index, cut) {
-      $('table > tbody', self.$el).append(cut.render());
-    });
-  }
-  if (this.deactivateCart) {
-    $.each(this.deactivateCart.cuts, function(index, cut) {
-      $('table > tbody', self.$el).append(cut.render());
+  if (this.model) {
+    $(this.model.cuts).each(function(index, cut) {
+      cut.active = self.active;
+
+      var cutView = new JingleCutView(cut);
+      self.cutViews.push(cutView);
+
+      $('table > tbody', self.groupView.$el).append(cutView.$el);
     });
   }
 };
 
-JingleGroup.prototype.destroy = function() {
-  $('table > tbody', this.$el).html('');
-};
-
-/*JingleGroup.prototype.addUpload = function() {
-  var $tfoot = $('table > tfoot', this.$el);
-  var $progressBar = $('.progressBarTemplate').clone().removeClass('progressBarTemplate');
-  $progressBar.appendTo($tfoot);
-  return $progressBar;
-};*/
+var JingleCutView = function(model) {
+  this.model = model;
 
-var JingleCart = function(number, title, groupName, group) {
-  this.number = number;
-  this.title = title;
-  this.groupName = groupName;
-  this.group = group;
+  this.$el = null;
 
-  this.cuts = [];
+  this.render();
 };
 
-JingleCart.prototype.addCut = function(cut) {
-  this.cuts.push(cut);
-};
+JingleCutView.prototype.render = function() {
+  var moveButton = $('<button class="btn btn-info btn-mini"><i class="icon-arrow-right icon-white"></i> Verschieben</button>');
+  var activateButton;
+  if (this.model.active) {
+    activateButton = $('<button class="btn btn-warning btn-mini"><i class="icon-minus icon-white"></i> Deactivieren</button>');
+  } else {
+    activateButton = $('<button class="btn btn-warning btn-mini"><i class="icon-plus icon-white"></i> Aktivieren</button>');
+  }
+  var deleteButton = $('<button class="btn btn-danger btn-mini"><i class="icon-trash icon-white"></i> Löschen</button>');
 
-JingleCart.prototype.removeCut = function(cut) {
   var self = this;
-  $.each(this.cuts, function(index, currentCut){
-    if(currentCut === cut) {
-      self.cuts.splice(index, 1);
-    }
+  moveButton.on('click', function() {
+    self.move();
+  });
+  activateButton.on('click', function() {
+    self.toggleActive();
+  });
+  deleteButton.on('click', function() {
+    self.removeSelf();
   });
-};
-
-var JingleCut = function(cart, cartNumber, name, description, active) {
-  this.cart = cart;
-  this.cartNumber = cartNumber;
-  this.number = name.substr(-3);
-  this.name = name;
-  this.description = description;
-
-  this.active = active;
 
-  this.$el = null;
+  this.$el = $('<tr>')
+      .attr('id', 'jingle-' + this.model.cartNumber + '-' + this.model.number)
+      .append($('<td>').text(this.model.name))
+      .append($('<td>').text(this.model.description))
+      .append(
+          $('<td>')
+              .append(moveButton)
+              .append(activateButton)
+              .append(deleteButton)
+      );
 };
 
-JingleCut.prototype.move = function() {
+JingleCutView.prototype.move = function() {
   var self = this;
-  var destinationCart = this.cartNumber;
+  var destinationCart = this.model.cartNumber;
+
   // todo: make this work for multiple groups
-  if (groupList.groups.length === 2) {
-    $(groupList.groups).each(function(index, group) {
-      if (self.active) {
-        if (self.cartNumber !== group.mainCart.number) {
+  if (jingleGroupListView.model.groups.length === 2) {
+    $(jingleGroupListView.model.groups).each(function(index, group) {
+      if (self.model.active) {
+        if (self.model.cartNumber !== group.mainCart.number) {
           destinationCart = group.mainCart;
         }
       } else {
-        if (self.cartNumber !== group.deactivateCart.number) {
+        if (self.model.cartNumber !== group.deactivateCart.number) {
           destinationCart = group.deactivateCart;
         }
       }
@@ -206,65 +195,29 @@ JingleCut.prototype.move = function() {
   }
 
   // todo: fix
-
-  rivendell.moveCut(this.cartNumber, this.number, destinationCart.number, function() {
-    self.cart.group.fetchCarts();
+  rivendell.moveCut(this.model.cartNumber, this.model.number, destinationCart.number, function() {
+    self.model.cart.group.fetchCarts();
     destinationCart.group.fetchCarts();
   });
 };
 
-JingleCut.prototype.toggleActive = function() {
-  var destinationCart = this.cartNumber;
-  if (this.active) {
+JingleCutView.prototype.toggleActive = function() {
+  var destinationCart = this.model.cartNumber;
+  if (this.model.active) {
     destinationCart++;
   } else {
     destinationCart--;
   }
   var self = this;
-  rivendell.moveCut(this.cartNumber, this.number, destinationCart, function() {
-    self.cart.group.fetchCarts();
+  rivendell.moveCut(this.model.cartNumber, this.model.number, destinationCart, function() {
+    self.model.cart.group.fetchCarts();
   });
 };
 
-JingleCut.prototype.removeSelf = function() {
+JingleCutView.prototype.removeSelf = function() {
   var self = this;
-  rivendell.removeCut(this.cartNumber, this.number, function() {
-    self.cart.removeCut(this);
+  rivendell.removeCut(this.model.cartNumber, this.model.number, function() {
+    self.model.cart.removeCut(this);
     self.$el.remove();
   });
 };
-
-JingleCut.prototype.render = function() {
-  var moveButton = $('<button class="btn btn-info btn-mini"><i class="icon-arrow-right icon-white"></i> Verschieben</button>');
-  var activateButton;
-  if (this.active) {
-    activateButton = $('<button class="btn btn-warning btn-mini"><i class="icon-minus icon-white"></i> Deactivieren</button>');
-  } else {
-    activateButton = $('<button class="btn btn-warning btn-mini"><i class="icon-plus icon-white"></i> Aktivieren</button>');
-  }
-  var deleteButton = $('<button class="btn btn-danger btn-mini"><i class="icon-trash icon-white"></i> Löschen</button>');
-
-  var self = this;
-  moveButton.on('click', function() {
-    self.move();
-  });
-  activateButton.on('click', function() {
-    self.toggleActive();
-  });
-  deleteButton.on('click', function() {
-    self.removeSelf();
-  });
-
-  this.$el = $('<tr>')
-    .attr('id', 'jingle-' + this.cartNumber + '-' + this.number)
-    .append($('<td>').text(this.name))
-    .append($('<td>').text(this.description))
-    .append(
-      $('<td>')
-        .append(moveButton)
-        .append(activateButton)
-        .append(deleteButton)
-    );
-
-  return this.$el;
-};
diff --git a/www/js/musicgrid.js b/www/js/musicgrid.js
index 221f489..d95fb1e 100644
--- a/www/js/musicgrid.js
+++ b/www/js/musicgrid.js
@@ -24,15 +24,10 @@
 
 var Rivendell = Rivendell || {};
 
-var rivendell = null;
 var musicgridView = null;
 var musicpoolModal = null;
 
 function musicgrid_init() {
-  rivendell = new Rivendell.Rivendell(auth_username, auth_token, '/rd-bin/rdxport.cgi');
-  rivendell.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
-  rivendell.setMusicgridEndpoint('/rh-bin/musicgrid.cgi');
-
   var musicgrid = new Rivendell.Musicgrid();
   musicgridView = new Rivendell.MusicgridView(musicgrid);
 
@@ -42,24 +37,37 @@ function musicgrid_init() {
 
 function musicgrid_cleanup() {
   musicpoolModal = null;
+
+  musicgridView.clean();
   musicgridView = null;
-  rivendell = null;
 }
 
 Rivendell.MusicgridView = function(model) {
   this.model = model;
 
+  this.$el = $('#app-musicgrid table');
+
   var self = this;
-  this.model.fetch(function() {
-    self.updateTable();
+  $(this.model).on('update', function() {
+    self.update();
   });
+  this.model.fetch();
 };
 
-Rivendell.MusicgridView.prototype.updateTable = function() {
-  var $table = $('#app-musicgrid table');
-  $('tr td', $table).html('').removeClass('clock').css('background-color', '').css('color', '').attr('title', null);
+Rivendell.MusicgridView.prototype.clean = function() {
+  $('tr td', this.$el)
+      .html('')
+      .removeClass('clock')
+      .css('background-color', '')
+      .css('color', '')
+      .attr('title', null);
+};
+
+Rivendell.MusicgridView.prototype.update = function() {
+  this.clean();
+
   $(this.model.clocks).each(function(index, clock) {
-    var $td = $('tr[data-dow="' + clock.dow + '"] td[data-hour="' + clock.hour +'"]', $table);
+    var $td = $('tr[data-dow="' + clock.dow + '"] td[data-hour="' + clock.hour +'"]', this.$el);
     $td.addClass('clock');
     $td.html(clock.name);
     $td.attr('title', clock.title);
@@ -76,10 +84,9 @@ Rivendell.MusicgridView.prototype.updateTable = function() {
     });
   });
 
-  $('tr td:not(.clock)', $table).off().on('click', function() {
+  $('tr td:not(.clock)', this.$el).off().on('click', function() {
     musicpoolModal.selectClock($(this).parent().data('dow'), $(this).data('hour'), null);
   });
-
 };
 
 Rivendell.MusicpoolModal = function(model) {
@@ -97,7 +104,7 @@ Rivendell.MusicpoolModal.prototype.selectClock = function(dow, hour, clockName)
   $('tbody', $modalBody).html('');
 
   var self = this;
-  this.model.fetch('musicpool', function() {
+  $(this.model).on('update', function() {
     $(self.model.groups).each(function(index, musicpool) {
       var $button = null;
       if (clockName === musicpool.clock) {
@@ -109,7 +116,7 @@ Rivendell.MusicpoolModal.prototype.selectClock = function(dow, hour, clockName)
       $button.on('click', function() {
         rivendell.setMusicgrid(dow, hour, musicpool.clock, function() {
           $('#musicpoolModal').modal('hide');
-          musicgrid.fetch();
+          musicgridView.modal.fetch();
         });
       });
 
@@ -121,4 +128,5 @@ Rivendell.MusicpoolModal.prototype.selectClock = function(dow, hour, clockName)
       $('tbody', $modalBody).append($tr);
     });
   });
+  this.model.fetch('musicpool');
 };
diff --git a/www/js/musicpools.js b/www/js/musicpools.js
index ce8e81a..2ccd27f 100644
--- a/www/js/musicpools.js
+++ b/www/js/musicpools.js
@@ -24,14 +24,10 @@
 
 var Rivendell = Rivendell || {};
 
-var rivendell = null;
 var importer = null;
 var musicpoolsView = null;
 
 function musicpools_init() {
-  rivendell = new Rivendell.Rivendell(auth_username, auth_token, '/rd-bin/rdxport.cgi');
-  rivendell.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
-
   importer = new Rivendell.Importer();
 
   var musicpools = new Rivendell.GroupList(rivendell);
@@ -51,13 +47,14 @@ Rivendell.MusicpoolsView = function(model) {
   this.currentPoolId = sessionStorage.getItem('currentPoolId');
 
   var self = this;
-  this.model.fetch('musicpool', function() {
+  $(this.model).on('update', function() {
     $(self.model.groups).each(function(index, musicpool) {
       var musicpoolView = new Rivendell.MusicpoolView(musicpool);
       self.musicpoolViews.push(musicpoolView);
     });
     self.updateSelector();
   });
+  this.model.fetch('musicpool');
 };
 
 Rivendell.MusicpoolsView.prototype.setCurrentPoolId = function(currentPoolId) {
@@ -134,7 +131,7 @@ Rivendell.MusicpoolView = function(model) {
 
 Rivendell.MusicpoolView.prototype.render = function() {
   var self = this;
-  this.model.fetchCarts(function() {
+  $(this.model).on('update', function() {
     self.$el = $('#hiddenTemplates .musicpoolTemplate').clone().removeClass('musicpoolTemplate');
     $('#app-musicpools .musicpoolContainer').html(self.$el);
 
@@ -143,29 +140,11 @@ Rivendell.MusicpoolView.prototype.render = function() {
 
     // todo
     /*$('.uploadButton', self.$el).on('click', function() {
-      importer.showUploadModal(self.model);
+      importer.openModal(self.model);
     });*/
 
   });
-};
-
-Rivendell.Musicpool.prototype.fetchCarts = function(success) {
-  var self = this;
-  rivendell.listCarts(this.groupName, 1, function(cartsXml, status, req) {
-    self.carts = [];
-
-    var dbs = $('cartList', cartsXml).children();
-    dbs.each(function(index, cartXml) {
-      var cart = new Rivendell.Cart(cartXml, self);
-
-      var cuts = $('cutList', cartXml).children();
-      cart.cuts.push(new Rivendell.Cut(cuts[0], cart));
-
-      self.carts.push(cart);
-    });
-
-    success();
-  });
+  this.model.fetchCarts();
 };
 
 Rivendell.MusicpoolCutView = function(model) {
diff --git a/www/js/rivendell.js b/www/js/rivendell.js
index 24ef2c7..89cf471 100644
--- a/www/js/rivendell.js
+++ b/www/js/rivendell.js
@@ -384,6 +384,26 @@ Rivendell.Group = function(groupName, description, lowcart, highcart, normlevel,
   this.carts = [];
 };
 
+Rivendell.Group.prototype.fetchCarts = function() {
+  var self = this;
+  rivendell.listCarts(this.groupName, 1, function(cartsXml, status, req) {
+    self.carts = [];
+
+    var dbs = $('cartList', cartsXml).children();
+    dbs.each(function(index, cartXml) {
+      var cart = new Rivendell.Cart(cartXml, self);
+
+      var cuts = $('cutList', cartXml).children();
+      cuts.each(function(index, cut) {
+        cart.cuts.push(new Rivendell.Cut(cut, cart));
+      });
+
+      self.carts.push(cart);
+    });
+    $(self).trigger('update');
+  });
+};
+
 Rivendell.Cart = function(number, title, groupName, group) {
   this.xml = null;
   
@@ -403,6 +423,19 @@ Rivendell.Cart = function(number, title, groupName, group) {
   this.cuts = [];
 };
 
+Rivendell.Cart.prototype.addCut = function(cut) {
+  this.cuts.push(cut);
+};
+
+Rivendell.Cart.prototype.removeCut = function(cut) {
+  var self = this;
+  $.each(this.cuts, function(index, currentCut){
+    if(currentCut === cut) {
+      self.cuts.splice(index, 1);
+    }
+  });
+};
+
 Rivendell.Cut = function(name, description, cart) {
   this.xml = null;
 
@@ -416,8 +449,8 @@ Rivendell.Cut = function(name, description, cart) {
     this.description = description;
     this.cart = cart;
   }
-  this.number = name.substr(-3);
-  this.cartNumber = cart.number;
+  this.number = this.name.substr(-3);
+  this.cartNumber = this.cart.number;
 
 
 };
diff --git a/www/js/rivendell.rh.js b/www/js/rivendell.rh.js
index 5268cc1..bc8252e 100644
--- a/www/js/rivendell.rh.js
+++ b/www/js/rivendell.rh.js
@@ -72,7 +72,7 @@ Rivendell.Rivendell.prototype.setMusicgrid = function(dow, hour, name, success)
   return $.post(this.musicgridEndpoint, command, success, "xml");
 };
 
-Rivendell.GroupList.prototype.fetch = function(type, success) {
+Rivendell.GroupList.prototype.fetch = function(type) {
   this.groups = [];
 
   var self = this;
@@ -100,9 +100,7 @@ Rivendell.GroupList.prototype.fetch = function(type, success) {
       }
     });
 
-    if (success) {
-      success();
-    }
+    $(self).trigger('update');
   });
 };
 
@@ -110,7 +108,7 @@ Rivendell.Musicgrid = function() {
   this.clocks = [];
 };
 
-Rivendell.Musicgrid.prototype.fetch = function(success) {
+Rivendell.Musicgrid.prototype.fetch = function() {
   this.clocks = [];
 
   var self = this;
@@ -119,9 +117,7 @@ Rivendell.Musicgrid.prototype.fetch = function(success) {
     dbs.each(function(index, clockXml) {
       self.clocks.push(new Rivendell.MusicgridClock(clockXml));
     });
-    if (success) {
-      success();
-    }
+    $(self).trigger('update');
   });
 };
 
diff --git a/www/js/shows.js b/www/js/shows.js
index 764e0c4..c325e01 100644
--- a/www/js/shows.js
+++ b/www/js/shows.js
@@ -24,7 +24,6 @@
 
 var Rivendell = Rivendell || {};
 
-var rivendell = null;
 var shows_currentid = null;
 var shows_list = [];
 var shows_current;
@@ -327,8 +326,6 @@ function shows_updateList(data, status, req) {
 }
 
 function shows_init() {
-  rivendell = new Rivendell.Rivendell(auth_username, auth_token, '/rd-bin/rdxport.cgi');
-  rivendell.setListDropboxesEndpoint('/rh-bin/listdropboxes.cgi');
   shows_currentid = sessionStorage.getItem("shows_currentid");
   shows_list = [];
   rivendell.listDropboxes('show', shows_updateList);
@@ -343,5 +340,4 @@ function shows_cleanup() {
   shows_list = [];
   shows_group_carts = {};
   shows_log_carts = [];
-  rivendell = null;
 }
-- 
cgit v0.10.2