From c4b1c97791dea652365201d5716e97fdc416bccb Mon Sep 17 00:00:00 2001 From: Anthony Truskinger Date: Thu, 20 Dec 2012 17:29:01 +1000 Subject: [PATCH] More work on annotation page. Got panel drawing -> model working decently. Midway through switching from an array structure to an hash structure for storing audio events - this is because the pprimary operation on the data will be a key-based lookup (rather than just index) This will enable moving/resizing before selected evebnt fires modified: app/assets/javascripts/angular/controllers/listen.js modified: app/assets/javascripts/angular/directives/directives.js modified: app/assets/stylesheets/_annotation_viewer.css.scss -- added transparent background modified: app/assets/templates/annotation_viewer.html modified: app/assets/templates/listen.html modified: lib/assets/javascripts/jquery.drawabox.js -- made unique numbers negative modified: app/assets/javascripts/app.js -- made a version of safe apply that uses the scope it is attached on --- .../javascripts/angular/controllers/listen.js | 22 ++------- .../angular/directives/directives.js | 48 ++++++++++++------- app/assets/javascripts/app.js | 12 +++++ .../stylesheets/_annotation_viewer.css.scss | 1 + app/assets/templates/annotation_viewer.html | 2 +- app/assets/templates/listen.html | 11 +++-- lib/assets/javascripts/jquery.drawabox.js | 12 +++-- 7 files changed, 65 insertions(+), 43 deletions(-) diff --git a/app/assets/javascripts/angular/controllers/listen.js b/app/assets/javascripts/angular/controllers/listen.js index 1af541ea..2af24de6 100644 --- a/app/assets/javascripts/angular/controllers/listen.js +++ b/app/assets/javascripts/angular/controllers/listen.js @@ -20,22 +20,6 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) { else { var recordingId = $scope.recordingId = $routeParams.recordingId; - -// -// $scope.recording = AudioRecording.get($routeParams); -// -// // HACK: -// $scope.recordingurl = "/media/" + recordingId + "_0_120_0_11025.mp3"; -// -// -// var spectrogramResource = $resource('/media/:recordingId', {recordingId: '@recordingId'}, { -// get: { method: 'GET', params: {recordingId: '@recordingId'}, isArray: false } -// }); -// $scope.spectrogram = spectrogramResource.get($routeParams); -// -// // HACK: -// $scope.spectrogram.url = "/media/" + recordingId + "_0_120_0_11025_512_g.png" + "?" + angularCopies.toKeyValue($scope.authTokenParams()); - $scope.model = {}; var formatPaths = function () { @@ -71,11 +55,12 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) { $scope.clearSelected = function() { - $scope.selectedAnnotation = {}; + $scope.model.selectedAudioEvents.length = 0; }; $scope.addAnnotation = function createAnnotation() { - var a = angular.copy(this.selectedAnnotation); + // BUG: ONLY SAVES FIRST ONE + var a = angular.copy(this. $scope.model.selectedAudioEvents[0]); // prep tags a.audio_event_tags_attributes = a.audioEventTags.map(function (v) {return {tag_id:v};}); @@ -89,7 +74,6 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) { // now update tag-list $scope.model.audioEvents.push(response); - $scope.selectedAnnotation = response; }, function createAnnotationFailure(response, getResponseHeaders) { diff --git a/app/assets/javascripts/angular/directives/directives.js b/app/assets/javascripts/angular/directives/directives.js index bc59150d..00d844a3 100644 --- a/app/assets/javascripts/angular/directives/directives.js +++ b/app/assets/javascripts/angular/directives/directives.js @@ -116,6 +116,7 @@ }; function resizeOrMove(audioEvent, box) { + if (audioEvent.__temporaryId__ === box.id) { audioEvent.startTimeSeconds = box.left || 0; audioEvent.highFrequencyHertz = box.top || 0; @@ -125,9 +126,14 @@ audioEvent.lowFrequencyHertz = (audioEvent.highFrequencyHertz + box.height) || 0; } else { - throw "Box ids do not match on resizing or move event"; + console.error("Box ids do not match on resizing or move event", audioEvent.__temporaryId__ , box.id); } } + function resizeOrMoveWithApply(scope, audioEvent, box) { + scope.$apply(function() { + resizeOrMove(audioEvent, box); + }) + } function touchUpdatedField(audioEvent) { audioEvent.updatedAt = new Date(); @@ -168,6 +174,8 @@ // // transform DOM // }, link: function (scope, element, attributes, controller) { + + var $element = $(element); // assign a unique id to scope scope.id = Number.Unique(); @@ -175,53 +183,61 @@ scope.$canvas = $element.find(".annotation-viewer img + div").first(); // init drawabox - scope.model.audioEvents = scope.model.audioEvents || []; + // we use hash (POJO) because most of our operations involve lookups + scope.model.audioEvents = scope.model.audioEvents || {}; + // store only the ids in this array (again for speed) scope.model.selectedAudioEvents = scope.model.selectedAudioEvents || []; scope.$canvas.drawabox({ "newBox": function (element, newBox) { var newAudioEvent = create(newBox, "a dummy id!"); - scope.model.audioEvents.push(newAudioEvent); - console.log("newBox", newBox, newAudioEvent); + scope.$apply(function() { + scope.model.audioEvents[newAudioEvent.__temporaryId__] = newAudioEvent; + console.log("newBox", newBox, newAudioEvent); + }); }, "boxSelected": function (element, selectedBox) { console.log("boxSelected", selectedBox); - var audioEvent = _.find(scope.model.audioEvents, function(value) { return value.__temporaryId__ === selectedBox.id}); + //var audioEvent = _.find(scope.model.audioEvents, function(value) { return value.__temporaryId__ === selectedBox.id}); // support for multiple selections - remove the clear scope.model.selectedAudioEvents.length = 0; - scope.model.selectedAudioEvents.push(audioEvent); + scope.model.selectedAudioEvents.push(selectedBox.id); }, "boxResizing": function (element, box) { console.log("boxResizing"); - resizeOrMove(scope.model.selectedAudioEvents[0], box); + resizeOrMoveWithApply(scope, scope.model.audioEvents[box.id], box); }, "boxResized": function (element, box) { console.log("boxResized"); - resizeOrMove(scope.model.selectedAudioEvents[0], box); + resizeOrMoveWithApply(scope, scope.model.audioEvents[box.id], box); }, "boxMoving": function (element, box) { console.log("boxMoving"); - resizeOrMove(scope.model.selectedAudioEvents[0], box); + resizeOrMoveWithApply(scope, scope.model.audioEvents[box.id], box); }, "boxMoved": function (element, box) { console.log("boxMoved"); - resizeOrMove(scope.model.selectedAudioEvents[0], box); + resizeOrMoveWithApply(scope, scope.model.audioEvents[box.id], box); }, "boxDeleted": function (element, deletedBox) { console.log("boxDeleted"); - // TODO: is this done by reference? does it even work?; - _(scope.model.audioEvents).reject(function (item) { - return item.id === deletedBox.id; - }); - _(scope.model.selectedAudioEvents).reject(function (item) { - return item.id === deletedBox.id; + scope.$apply(function(){ + // TODO: is this done by reference? does it even work?; + _(scope.model.audioEvents).reject(function (item) { + return item.id === deletedBox.id; + }); + _(scope.model.selectedAudioEvents).reject(function (item) { + return item.id === deletedBox.id; + }); }); + + } }); diff --git a/app/assets/javascripts/app.js b/app/assets/javascripts/app.js index ebfd0724..c6a86fa0 100644 --- a/app/assets/javascripts/app.js +++ b/app/assets/javascripts/app.js @@ -141,6 +141,18 @@ var bawApp = (function (undefined) { } }; + $rootScope.$safeApply2 = function (fn) { + var $scope = this || $rootScope; + fn = fn || function () {}; + + if ($scope.$$phase) { + fn(); + } + else { + $scope.$apply(fn); + } + }; + $rootScope.$on("$routeChangeError", function (event, current, previous, rejection) { console.warn("route changing has failed... handle me some how"); //change this code to handle the error somehow diff --git a/app/assets/stylesheets/_annotation_viewer.css.scss b/app/assets/stylesheets/_annotation_viewer.css.scss index 37925373..b8d59529 100644 --- a/app/assets/stylesheets/_annotation_viewer.css.scss +++ b/app/assets/stylesheets/_annotation_viewer.css.scss @@ -53,6 +53,7 @@ baw-annotation-viewer { border-width: $border-width; border-color: nth($master-highlight, 2); position: absolute; + background-color: rgba(255,255,255,0.05); &[data-selected="true"] { border-color: nth($master-complementary, 2); diff --git a/app/assets/templates/annotation_viewer.html b/app/assets/templates/annotation_viewer.html index 1317bfd6..cc2380e6 100644 --- a/app/assets/templates/annotation_viewer.html +++ b/app/assets/templates/annotation_viewer.html @@ -6,7 +6,7 @@
-
+
diff --git a/app/assets/templates/listen.html b/app/assets/templates/listen.html index 6f07bdde..392af261 100644 --- a/app/assets/templates/listen.html +++ b/app/assets/templates/listen.html @@ -90,8 +90,13 @@

Annotations

- {{ae.id}} - + {{ae.id}} + + new annotation + + + {{ae.audioRecording.uuid}} {{ae.createdAt}} {{ae.creatorId}} @@ -100,7 +105,7 @@

Annotations

{{ae.updatedAt}} {{ae.updaterId}} {{t.tagId}}, - {{ae.starttimeSeconds}} - {{ae.endTimeSeconds}} + {{ae.startTimeSeconds}} - {{ae.endTimeSeconds}} {{ae.lowFrequencyHertz}} - {{ae.highFrequencyHertz}} diff --git a/lib/assets/javascripts/jquery.drawabox.js b/lib/assets/javascripts/jquery.drawabox.js index c62313af..a5fefa4b 100644 --- a/lib/assets/javascripts/jquery.drawabox.js +++ b/lib/assets/javascripts/jquery.drawabox.js @@ -122,7 +122,7 @@ var closeIconTemplate = ''; - var uniqueId = Number.Unique(); + var uniqueId = -1 *Number.Unique(); $('.boxItem').attr(SELECTED_ATTRIBUTE, false); var newId = "boxItem_" + uniqueId; contextData.currentMouseDragBoxId = newId; @@ -196,6 +196,8 @@ return; } + // box must be selected + //var wasMouseDownSet = dataMouseMove.mousedown; var currentBoxId = dataMouseMove.currentMouseDragBoxId; @@ -216,8 +218,8 @@ var xdiff = Math.abs(currentPos.x - startClickPos.x); var ydiff = Math.abs(currentPos.y - startClickPos.y); - // this is wrong - it incorrectly gets hypotenuseal distance, not the length of the edges - //var distance = Math.sqrt((xdiff * xdiff) + (ydiff * ydiff)); + + // minimum dimensions before valid box var distance = Math.min(xdiff, ydiff); if (!currentBoxId) { @@ -293,7 +295,7 @@ var maxBoxes = parseInt(options.maxBoxes); if (isNaN(maxBoxes) && maxBoxes < 1) { - throw new Error("Max boxs must be an int greater than zero (or undefined)"); + throw new Error("Max boxes must be an int greater than zero (or undefined)"); } if (maxBoxes < options.initialBoxes.length) { throw "max boxes must be greater than the initial number of boxes that are to be drawn"; @@ -315,6 +317,8 @@ } + + return this.each(function () { var $this = $(this);