Skip to content

Commit

Permalink
Work on annotation page.
Browse files Browse the repository at this point in the history
Fixed selection bug (WARN: GIANT ARSE HACK)

Closes #78, Works, though depends on #79 to do anything useful

Closes #75, done a few revions ago. Unit calculations are based off image dimensions

Closes #65, done a few revisions ago

Closes #33, implemented with request animation loop. bit slow in FF. spills over edge sometimes, but basically works

modified:   app/assets/javascripts/angular/controllers/annotation_viewer.js
	-- audio position line indicator work

modified:   app/assets/javascripts/angular/controllers/listen.js
	-- added various functions for summary statistics

modified:   app/assets/javascripts/angular/directives/directives.js
	-- fixed bawChecked directrive, wrote bawTranslateX, and patched ngAudio (for position line updating)

modified:   app/assets/javascripts/angular/filters/filters.js
	-- added moment and timespan filters for nice formatting

modified:   lib/assets/javascripts/functions.js
modified:   app/assets/javascripts/app.js
modified:   app/assets/javascripts/angular/services/services.js
	-- refactored functions to get rid off global declarations and fixed up associated references

modified:   app/assets/stylesheets/_base.css.scss
	-- added colors for time type hints

modified:   app/assets/stylesheets/_layout.css.scss
	-- added pseudo float clearing class

modified:   app/assets/stylesheets/partials/_annotation_viewer.css.scss
	-- added css for audio position line

modified:   app/assets/stylesheets/partials/_listen.css.scss
	-- formatted new time values

modified:   app/assets/stylesheets/partials/_time_formats.css.scss
	-- fixed of hints for side and bottom hints

modified:   app/assets/templates/annotation_viewer.html

modified:   app/assets/templates/listen.html
	-- added lots of contextual time stuff. fixed binding for baw checked
  • Loading branch information
atruskie committed Feb 8, 2013
1 parent 052b187 commit 4e3535c
Show file tree
Hide file tree
Showing 14 changed files with 541 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ function AnnotationViewerCtrl($scope, $element, $attrs, $transclude) {
return $scope.model.converters.secondsToPixels(audioEvent.startTimeSeconds);
};

$scope.positionLine = function () {
return $scope.model.converters.secondsToPixels($scope.model.audioElement.position);
};


// updated in directive
$scope.model.converters = $scope.model.converters || {};
Expand All @@ -47,6 +51,7 @@ function Annotation(localIdOrResource, audioRecordingId) {

this.__temporaryId__ = localId || Number.Unique();
this._selected = false;
this.audioEventTags = [];

if (localId) {
this.audioRecordingId = audioRecordingId;
Expand All @@ -59,7 +64,7 @@ function Annotation(localIdOrResource, audioRecordingId) {
this.isReference = false;
this.lowFrequencyHertz = 0.0;
this.startTimeSeconds = 0.0;
this.audioEventTags = [];

}

if (resource) {
Expand Down
71 changes: 64 additions & 7 deletions app/assets/javascripts/angular/controllers/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
* @constructor
* @param Tag
* @param Media
* @param $route
*/
function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) {
function ListenCtrl($scope, $resource, $routeParams, $route, Media, AudioEvent, Tag) {
var CHUNK_DURATION_SECONDS = 30.0;
function getMediaParameters() {
return {
Expand All @@ -21,7 +22,7 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) {
}
}

$scope.errorState = !GUID_REGEXP.test($routeParams.recordingId);
$scope.errorState = !baw.GUID_REGEXP.test($routeParams.recordingId);

if ($scope.errorState) {
console.warn("Invalid guid specified in route... page rendering disabled");
Expand Down Expand Up @@ -104,18 +105,74 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) {
};


$scope.startOffset = function() {
$scope.startOffsetChunk = function() {
if (!$scope.model.media) {
return;
}

return moment($scope.model.media.original.recordedDate).add({seconds: $scope.model.media.startOffset});
return baw.secondsToDurationFormat($scope.model.media.startOffset);
};
$scope.endOffset = function() {
$scope.endOffsetChunk = function() {
if (!$scope.model.media) {
return;
}
return moment($scope.model.media.original.recordedDate).add({seconds: $scope.model.media.endOffset});
return baw.secondsToDurationFormat($scope.model.media.endOffset);
};

$scope.durationChunk = function() {
if (!$scope.model.media) {
return;
}
return baw.secondsToDurationFormat($scope.model.media.endOffset - $scope.model.media.startOffset);
};

$scope.currentOffsetChunk = function() {
var offset = 0;
if ($scope.model.audioElement) {
offset = $scope.model.audioElement.position;
}
return baw.secondsToDurationFormat(offset);
};

$scope.currentOffsetRecording = function() {
var offset = 0;
if ($scope.model.audioElement) {
offset = $scope.model.audioElement.position;
}

if (!$scope.model.media) {
return "";
}

var start = parseFloat($scope.model.media.startOffset);

return baw.secondsToDurationFormat(start + offset);
};

$scope.absoluteDateChunkStart = function() {
if (!$scope.model.media || !$scope.model.media.original) {
return;
}

var base = moment($scope.model.media.original.recordedDate);
var offset = base.add({seconds: $scope.model.media.startOffset});
return offset;
};

$scope.createNavigationHref = function(linkType, stepBy) {

if (!angular.isNumber(stepBy)) {
stepBy = CHUNK_DURATION_SECONDS;
}

if (linkType === "previous") {
return "/listen/" + recordingId + "/start=" + ($routeParams.start - stepBy) + "/end=" + ($routeParams.end - stepBy);
}
else if (linkType === "next") {
return "/listen/" + recordingId + "/start=" + ($routeParams.start + stepBy) + "/end=" + ($routeParams.end + stepBy) ;
}

throw "Invalid link type specified in createNavigationHref";
};

$scope.clearSelected = function() {
Expand Down Expand Up @@ -181,4 +238,4 @@ function ListenCtrl($scope, $resource, $routeParams, Media, AudioEvent, Tag) {
}
}

ListenCtrl.$inject = ['$scope', '$resource', '$routeParams', 'Media', 'AudioEvent', 'Tag'];
ListenCtrl.$inject = ['$scope', '$resource', '$routeParams', '$route', 'Media', 'AudioEvent', 'Tag'];
148 changes: 93 additions & 55 deletions app/assets/javascripts/angular/directives/directives.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function () {
(function (undefined) {
var bawds = angular.module('bawApp.directives', []);

bawds.directive('bawRecordInformation', function () {
Expand Down Expand Up @@ -92,11 +92,11 @@
var valid = true;
if (isList) {
for (var i = 0; i < viewValue.length && valid; i++) {
valid = GUID_REGEXP.test(viewValue[i]);
valid = baw.GUID_REGEXP.test(viewValue[i]);
}
}
else {
valid = GUID_REGEXP.test(viewValue);
valid = baw.GUID_REGEXP.test(viewValue);
}

if (valid) {
Expand Down Expand Up @@ -253,15 +253,15 @@
};

// create the listener - the actual callback
var listenerFunc = function (value) {
var listenerFunc = function audioEventToBoxWatcher(value) {

if (value) {
if (scope.__lastDrawABoxEditId__ === value.__temporaryId__) {
scope.__lastDrawABoxEditId__ = undefined;
return;
}

console.log("audioEvent watcher fired");
console.log("audioEvent watcher fired", value.__temporaryId__, value._selected);

// TODO: SET UP CONVERSIONS HERE
var top = scope.model.converters.hertzToPixels(value.highFrequencyHertz),
Expand Down Expand Up @@ -411,7 +411,7 @@
'durationchange': function (event) {
scope.$safeApply2(function () {
if (attributes.ngAudio) {
var target = scope.$eval(attributes.ngAudio)
var target = scope.$eval(attributes.ngAudio);
if (target) {
target.duration = element.duration;
return;
Expand All @@ -435,94 +435,132 @@
value();
}
});

// position binding - reverse (element to model)
// TODO: we can optimise this, it does not always need to be running
window.requestAnimationFrame(function audioElementPositionRAF() {
// need to request each new frame
window.requestAnimationFrame(audioElementPositionRAF);
if (attributes.ngAudio) {
var target = scope.$eval(attributes.ngAudio);
if (target) {
var position = element.currentTime;
if (target.position != position) {
scope.$safeApply2(function () {
target.position = position;
});
}
}
}
}, elements[0]);

}
}
});

/**
* A cross record element checker
*/
bawds.directive('bawChecked', function () {
bawds.directive('bawChecked', ['$parse', function ($parse) {

// a cache of elements for each radio group
var library = {};


return {
restict: 'A',
require: 'ngModel',
link: function radioInputType(scope, element, attr, ctrl) {
link: function radioInputType(scope, element, attr) {
// make the name unique, if not defined
if (angularCopies.isUndefined(attr.name)) {
if (baw.angularCopies.isUndefined(attr.name)) {
element.attr('name', Number.Unique());
}

// add element to cache group
library[attr.name] = [];
var getter = $parse(attr.bawChecked);
var assigner = getter.assign;

function fixLast(negativeModelValue) {
if (library[attr.name].length > 0) {
var oldScope = library[attr.name].pop();
// store elements from same group to enable mass updates
library[attr.name] = library[attr.name] || [];
library[attr.name].push({e: element, s: scope, g: getter, a: assigner});

oldScope[0].$safeApply2(function () {
// for the other elements, ensure consistent state
oldScope[1].$setViewValue(negativeModelValue);
});
}

library[attr.name].push([scope, ctrl]);
}


// reverse binding (from element to model)

function updateModel() {
// if value is defined, then when checked, set to value
// otherwise just use true/false
var negativeModelValue;
var newModelValue;
var checked = element[0].checked;
if (angularCopies.isUndefined(attr.value)) {
newModelValue = checked;
negativeModelValue = !checked;
// forward binding from model
// aggressively updates all elements
scope.$watch(getter, function (newValue, oldValue) {
if (newValue) {
// if a true value is set, aggressively set others to false
angular.forEach(library[attr.name], function (libraryItem) {
if (libraryItem.e === element) {
libraryItem.e[0].checked = true;
} else {
libraryItem.e[0].checked = false;
libraryItem.a(libraryItem.s, false);
}
});
}
else {
newModelValue = checked ? attr.value : null;
negativeModelValue = checked ? null : attr.value;
// if it's false, just make sure it is checked right (don't perpetuate the loop)
element[0].checked = false;
}
});

// reverse bindings, elements to model
function updateModel(event) {
var isChecked = event.target.checked;

if (newModelValue != ctrl.$viewValue) {
fixLast(negativeModelValue);
var newest = library[attr.name][library[attr.name].length - 1];
// STUPID ARSE HACK... for some reason the latest item although selected does not properly update
// the 'last' value on the watch. so when it is first set to false nothing happens.
// therefore *force* a change here by doing a separate apply with a bullshit value
scope.$apply(function () {
newest.a(newest.s, null);
});

scope.$apply(function () {
// for the current element change it
ctrl.$setViewValue(newModelValue);
});
}
scope.$apply(function () {
assigner(scope, isChecked);

if (newest.e[0] != event.target) {
newest.a(newest.s, !isChecked);
}
});

}

element.bind('click', updateModel);
// element.bind('change', updateModel);

// forward binding (from model to element)
ctrl.$render = function () {
var value = angularCopies.isUndefined(attr.value) ? true : attr.value;
}
}
}]);

element[0].checked = (value == ctrl.$viewValue);

if (element[0].checked === true) {
fixLast(false);
}
};
/**
* A directive for binding the position of an element to the model.
* The binding uses translate transforms.
*
* ONLY one way binding supported at the moment
*/
bawds.directive('bawTranslateX', function () {

var transformSupport = Modernizr.csstransforms;
var transformProperty = 'left';
if (transformSupport) {
transformProperty = Modernizr.prefixed('transform');
}

attr.$observe('value', ctrl.$render);

return {
restrict: 'A',
link: function (scope, elements, attributes, controller) {
var element = elements[0];

// lastly cache any new items
// library[attr.name].push([scope, ctrl]);
scope.$watch(attributes.bawTranslateX, function (newValue, oldValue) {
if (transformSupport) {
element.style[transformProperty] = 'translateX(' + newValue.toFixed(3) + 'px)';
}
else {
element.style[transformProperty] = '' + newValue.toFixed(3) + 'px';
}
});
}
}
});
Expand Down
18 changes: 18 additions & 0 deletions app/assets/javascripts/angular/filters/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,22 @@
}
});


/**
* Format a given value to the with the site's default timespan formatter
* assumes input is in seconds
*/
bawfs.filter('formatTimeSpan', function() {
return function(input) {

if (input) {
return baw.secondsToDurationFormat(input);
}
else {
return '';
}

}
});

})();
4 changes: 2 additions & 2 deletions app/assets/javascripts/angular/services/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@
}

function openIdLogin(url) {
var popPath = "/security/auth/open_id?openid_url=" + angularCopies.fixedEncodeURIComponent(url);
popUpWindow(popPath, 700, 500, function (data) {
var popPath = "/security/auth/open_id?openid_url=" + baw.angularCopies.fixedEncodeURIComponent(url);
baw.popUpWindow(popPath, 700, 500, function (data) {
data = data || {};

railsFieldRenamingInterceptor().core(data);
Expand Down
Loading

0 comments on commit 4e3535c

Please sign in to comment.