Skip to content

Commit

Permalink
Worked on reverse binding the annotation editor
Browse files Browse the repository at this point in the history
modified:   app/assets/javascripts/angular/directives/directives.js
	-- set up watcher binding for reverse binding to drawabox

modified:   app/assets/javascripts/angular/services/services.js
	-- added safe guard for persona init when library not found

modified:   app/assets/templates/listen.html
modified:   app/assets/stylesheets/_listen.css.scss
	-- added gridview for annotation editing

modified:   lib/assets/javascripts/jquery.drawabox.js
	-- fixed a few bugs for reverse binding
  • Loading branch information
atruskie committed Dec 31, 2012
1 parent 6fcd990 commit cb2a691
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 120 deletions.
51 changes: 48 additions & 3 deletions app/assets/javascripts/angular/directives/directives.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,48 @@
return audioEvent;
}

/**
* Create an watcher for an audio event model.
* The purpose is to allow for reverse binding from model -> drawabox
* NB: interestingly, these watchers are bound to array indexes... not the objects in them.
* this means the object is not coupled to the watcher and is not affected by any operation on it.
* @param scope
* @param array
* @param index
* @param drawaboxInstance
*/
function registerWatcher(scope, array, index, drawaboxInstance) {

// create the watcher
var watcherFunc = function() {
return array[index];
};

// create the listener - the actual callback
var listenerFunc = function(value) {
console.log("audioEvent watcher fired");

// TODO: SET UP CONVERSIONS HERE
var top = value.highFrequencyHertz,
left = value.startTimeSeconds,
width = value.endTimeSeconds - value.startTimeSeconds,
height = value.highFrequencyHertz - value.lowFrequencyHertz;

drawaboxInstance.drawabox('setBox', value.__temporaryId__, top, left, height, width, undefined);
};

// tag both for easy removal later
var tag = "index" + index.toString()
watcherFunc.__drawaboxWatcherForAudioEvent = tag;
listenerFunc.__drawaboxWatcherForAudioEvent = tag;

// don't know if I need deregisterer or not - use this to stop listening...
// --
// note the last argument sets up the watcher for compare equality (not reference).
// this may cause memory / performance issues if the model gets too big later on
var deregisterer = scope.$watch(watcherFunc, listenerFunc, true)
}

return {
restrict: 'AE',
scope: {
Expand All @@ -218,16 +260,15 @@
// },
link: function (scope, element, attributes, controller) {


var $element = $(element);

// assign a unique id to scope
scope.id = Number.Unique();

scope.$canvas = $element.find(".annotation-viewer img + div").first();

// init drawabox
scope.model.audioEvents = scope.model.audioEvents || [];
// store only the ids in this array (again for speed)
scope.model.selectedAudioEvents = scope.model.selectedAudioEvents || [];


Expand All @@ -240,7 +281,11 @@
scope.$apply(function() {
scope.model.audioEvents.push( newAudioEvent);

element[0].annotationViewerIndex = scope.model.audioEvents.length - 1;
var annotationViewerIndex = scope.model.audioEvents.length - 1;
element[0].annotationViewerIndex = annotationViewerIndex;

// register for reverse binding
registerWatcher(scope, scope.model.audioEvents, annotationViewerIndex, scope.$canvas);

console.log("newBox", newBox, newAudioEvent);
});
Expand Down
35 changes: 20 additions & 15 deletions app/assets/javascripts/angular/services/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,22 +160,27 @@
}

// Navigator is the persona global object
navigator.id.watch({
loggedInUser: null,
onlogin: function (assertion) {
// A user has logged in! Here you need to:
// 1. Send the assertion to your backend for verification and to create a session.
// 2. Update your UI.
$http({method: 'POST', url: '/security/auth/browser_id/callback', data: {assertion: assertion}})
.success(Authenticator.loginSuccess)
.error(Authenticator.loginFailure);
if (navigator) {
navigator.id.watch({
loggedInUser: null,
onlogin: function (assertion) {
// A user has logged in! Here you need to:
// 1. Send the assertion to your backend for verification and to create a session.
// 2. Update your UI.
$http({method: 'POST', url: '/security/auth/browser_id/callback', data: {assertion: assertion}})
.success(Authenticator.loginSuccess)
.error(Authenticator.loginFailure);

},
// A user has logged out! Here you need to:
// Tear down the user's session by redirecting the user or making a call to your backend.
// Also, make sure loggedInUser will get set to null on the next page load.
onlogout: signOut
});
},
// A user has logged out! Here you need to:
// Tear down the user's session by redirecting the user or making a call to your backend.
// Also, make sure loggedInUser will get set to null on the next page load.
onlogout: signOut
});
}
else {
console.error("Unable to start Persona authentication binding. This is usually caused by a lack of internet.")
}

function openIdLogin(url) {
var popPath = "/security/auth/open_id?openid_url=" + angularCopies.fixedEncodeURIComponent(url);
Expand Down
16 changes: 16 additions & 0 deletions app/assets/stylesheets/_listen.css.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@


#listenEditContainer {

}


#listenEditColumn {
width: 250px;
display: inline-block;
}

#listenTableColumn {
display: inline-block;
vertical-align: top;
}
175 changes: 86 additions & 89 deletions app/assets/templates/listen.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,99 +19,96 @@ <h3>Audio Controls</h3>
<source ng-src="{{media.audioUrl}}" src="">
Your browser does not support the audio element.
</audio>

<h3>
Annotation edit
</h3>

<span>Selected elements: {{model.selectedAudioEvents.length}}</span>
<form name="annotationEditForm" ng-repeat="selectedEvent in model.selectedAudioEvents">
<label>
<span>Tags</span>
<em>--TODO: support for proper tagging control--</em><br/>
<select ng-multiple="true" tags multiple ui-select2="{allowClear:true}"
ng-model="selectedEvent.audioEventTags">
<option ng-repeat="t in tags" value="{{t.id}}">{{t.text}} <span> <!-- ng-show={{t.type_of_tag}}-->,
<div id="listenEditContainer">
<div id="listenEditColumn">
<h3>
Annotation edit
</h3>
<span>Selected elements: {{model.selectedAudioEvents.length}}</span>

<form id="annotationEditForm" name="annotationEditForm"
ng-repeat="selectedEvent in model.selectedAudioEvents">
<label>
<span>Tags</span>
<select ng-multiple="true" tags multiple ui-select2="{allowClear:true}"
ng-model="selectedEvent.audioEventTags">
<option ng-repeat="t in tags" value="{{t.id}}">{{t.text}} <span> <!-- ng-show={{t.type_of_tag}}-->,
{{t.typeOfTag}} <!-- --></span></option>
</select>


</label>

<label>
<span>Duration (seconds)</span></label>
<input type="number" min="{{limits.timeMin}}" max="{{limits.timeMax}}" required
ng-model="selectedEvent.startTimeSeconds"/>
&nbsp;to&nbsp;
<input type="number" min="{{limits.timeMin}}" max="{{limits.timeMax}}" required
ng-model="selectedEvent.endTimeSeconds"/>

<label>
<span>Frequency</span> </label>
<input type="number" min="{{limits.freqMin}}" max="{{limits.freqMax}}" required
ng-model="selectedEvent.lowFrequencyHertz"/>
&nbsp;to&nbsp;
<input type="number" min="{{limits.freqMin}}" max="{{limits.freqMax}}" required
ng-model="selectedEvent.highFrequencyHertz"/>


<fieldset>
<legend>Annotation options</legend>
<label>
<input type="checkbox" ng-model="selectedEvent.isReference">&nbsp;Is reference tag?
</label>
</fieldset>
<p>
data for tag: ({{selectedEvent.startTimeSeconds}}, {{selectedEvent.endTimeSeconds}});
({{selectedEvent.lowFrequencyHertz}}, {{selectedEvent.highFrequencyHertz}});
{{selectedEvent.isReference}};
<span ng-bind="selectedEvent.tags"></span>
</p>
<button type="button" ng-click="reset()">Clear</button>
<button ng-click="addAnnotation()">Add annotation</button>

</form>

<h3>Annotations</h3>
<table>
<thead>
<tr>
<th>Annotation ID</th>
<th>Audio Recording</th>
<th>Created At</th>
<th>Created By</th>
<th>Deleted At</th>
<th>Deleted By</th>
<th>Updated At</th>
<th>Updated By</th>
<th>Tags</th>
<th>Time Range</th>
<th>Frequency Range</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ae in model.audioEvents">
<td colspan="{{ae.id && 1 || 2}}">
{{ae.id}}
</select>
</label>
<label>
<span>Duration (seconds)</span></label>
<input type="number" min="{{limits.timeMin}}" max="{{limits.timeMax}}" required
ng-model="selectedEvent.startTimeSeconds"/>
&nbsp;to&nbsp;
<input type="number" min="{{limits.timeMin}}" max="{{limits.timeMax}}" required
ng-model="selectedEvent.endTimeSeconds"/>
<label>
<span>Frequency</span> </label>
<input type="number" min="{{limits.freqMin}}" max="{{limits.freqMax}}" required
ng-model="selectedEvent.lowFrequencyHertz"/>
&nbsp;to&nbsp;
<input type="number" min="{{limits.freqMin}}" max="{{limits.freqMax}}" required
ng-model="selectedEvent.highFrequencyHertz"/>
<fieldset>
<legend>Annotation options</legend>
<label>
<input type="checkbox" ng-model="selectedEvent.isReference">&nbsp;Is reference tag?
</label>
</fieldset>
<p>
data for tag: ({{selectedEvent.startTimeSeconds}}, {{selectedEvent.endTimeSeconds}});
({{selectedEvent.lowFrequencyHertz}}, {{selectedEvent.highFrequencyHertz}});
{{selectedEvent.isReference}};
<span ng-bind="selectedEvent.tags"></span>
</p>
<button type="button" ng-click="reset()">Clear</button>
<button ng-click="addAnnotation()">Add annotation</button>

</form>
</div>
<div id="listenTableColumn">
<h3>Annotations</h3>
<table>
<thead>
<tr>
<th>Annotation ID</th>
<th>Audio Recording</th>
<th>Created At</th>
<th>Created By</th>
<th>Deleted At</th>
<th>Deleted By</th>
<th>Updated At</th>
<th>Updated By</th>
<th>Tags</th>
<th>Time Range</th>
<th>Frequency Range</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ae in model.audioEvents">
<td colspan="{{ae.id && 1 || 2}}">
{{ae.id}}
<span ng-hide="ae.id">
<em>new annotation</em>
</span>
</td>
<td ng-show="ae.id" ><a ng-href="/recordings/{{ae.audioRecording.uuid}}"
class="short-guid">{{ae.audioRecording.uuid}}</a></td>
<td>{{ae.createdAt}}</td>
<td>{{ae.creatorId}}</td>
<td>{{ae.deletedAt}}</td>
<td>{{ae.deleterId}}</td>
<td>{{ae.updatedAt}}</td>
<td>{{ae.updaterId}}</td>
<td><span ng-repeat="t in ae.audioEventTags">{{t.tagId}},</span></td>
<td>{{ae.startTimeSeconds}} - {{ae.endTimeSeconds}}</td>
<td>{{ae.lowFrequencyHertz}} - {{ae.highFrequencyHertz}}</td>
</tr>
</tbody>
</table>

</td>
<td ng-show="ae.id"><a ng-href="/recordings/{{ae.audioRecording.uuid}}"
class="short-guid">{{ae.audioRecording.uuid}}</a></td>
<td>{{ae.createdAt}}</td>
<td>{{ae.creatorId}}</td>
<td>{{ae.deletedAt}}</td>
<td>{{ae.deleterId}}</td>
<td>{{ae.updatedAt}}</td>
<td>{{ae.updaterId}}</td>
<td><span ng-repeat="t in ae.audioEventTags">{{t.tagId}},</span></td>
<td>{{ae.startTimeSeconds}} - {{ae.endTimeSeconds}}</td>
<td>{{ae.lowFrequencyHertz}} - {{ae.highFrequencyHertz}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<baw-debug-info></baw-debug-info>
</div>
</div>
Loading

0 comments on commit cb2a691

Please sign in to comment.