diff --git a/Gruntfile.js b/Gruntfile.js index daa62d34..87625a92 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,6 @@ var modRewrite = require('connect-modrewrite'), - path = require('path'); + path = require('path'), + _ = require('lodash'); module.exports = function (grunt) { @@ -168,20 +169,47 @@ module.exports = function (grunt) { src: [ '**' ], dest: '<%= build_dir %>/assets/', cwd: 'src/assets', - expand: true + expand: true, + nonull: true } ] }, build_vendor_assets: { - files: [ - { - src: [ '<%= vendor_files.assets %>' ], + files: (function () { + var result = []; + + var template = { + //src: [ '<%= vendor_files.assets %>' ], dest: '<%= build_dir %>/assets/', cwd: '.', expand: true, - flatten: true - } - ] + flatten: true, + nonull: true + }; + + userConfig.vendor_files.assets.forEach(function (value, index, source) { + var kind = grunt.util.kindOf(value); + var templateCopy = _.clone(template); + if (kind === "string") { + templateCopy.src = value; + result.push(_.extend(template, base)); + } + else { + // here we assume it is a special specification func + var transformedTemplate = value.call(null, templateCopy); + + if (!transformedTemplate) { + throw "Copy:build_vendor_assets:transformTemplate: expected object, got " + transformedTemplate + " instead"; + } + + result.push(transformedTemplate); + } + }); + + grunt.log.debug("Vendor assets compilation result:\n" + JSON.stringify(result, undefined, 2)); + + return result; + })() }, build_appjs: { options: { @@ -239,9 +267,12 @@ module.exports = function (grunt) { * together. */ build_css: { + options: { + banner: '<%= meta.banner %>' + }, + nonull: true, src: [ '<%= vendor_files.css %>', - /*'<%= recess.build.dest %>',*/ '<%= build_dir %>/assets/styles/*.css' ], dest: '<%= sassDest %>' @@ -254,6 +285,7 @@ module.exports = function (grunt) { options: { banner: '<%= meta.banner %>' }, + nonull: true, src: [ '<%= vendor_files.js %>', 'module.prefix', diff --git a/build.config.js b/build.config.js index 9dacd45f..56aa3209 100644 --- a/build.config.js +++ b/build.config.js @@ -105,9 +105,18 @@ module.exports = { 'vendor/underscore/underscore.js' ], css: [ - 'vendor/hint.css/hint.css' + 'vendor/hint.css/hint.css', + // TODO: remove bloat + 'vendor/jquery-ui/themes/redmond/jquery-ui.css' ], assets: [ + // jquery-ui is stoopid, special case + function(template) { + template.src = 'vendor/jquery-ui/themes/redmond/images/**'; + template.dest += "styles/images/"; + + return template; + } ] } }; diff --git a/package.json b/package.json index 79b739fc..5c67a85d 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "grunt-bump": "0.0.6", "grunt-contrib-connect": "~0.5.0", "connect-modrewrite": "~0.5.7", - "grunt-sass": "~0.7.0" + "grunt-sass": "~0.7.0", + "lodash": "~2.2.1" }, "private": true } diff --git a/src/app/annotationViewer/_annotation_viewer.scss b/src/app/annotationViewer/_annotation_viewer.scss index 392ea5ff..1e6d284a 100644 --- a/src/app/annotationViewer/_annotation_viewer.scss +++ b/src/app/annotationViewer/_annotation_viewer.scss @@ -33,7 +33,7 @@ baw-annotation-viewer { line-height: $standard-line-height; li { - border-bottom: red solid 1px; + border-bottom: $tagAlignmentLine solid 1px; margin-left: -4px; position: absolute; bottom: -1px; @@ -91,7 +91,7 @@ baw-annotation-viewer { position: absolute; top: 0; left: 0; - -webkit-transform: translatex(0px); + @include vendor-prefix(transform, translatex(0 px)); } } @@ -111,9 +111,9 @@ baw-annotation-viewer { overflow: visible; &:before { - @if $DEBUG == false { - visibility: collapse; - } +// @if $DEBUG == false { +// visibility: collapse; +// } border-left: $tagAlignmentLine solid 1px; height: 256px; position: relative; @@ -121,6 +121,8 @@ baw-annotation-viewer { left: -1px; display: inline-block; content: ""; + width: 100%; + visibility: hidden; } &[data-selected="true"] { @@ -137,23 +139,22 @@ baw-annotation-viewer { & .close-icon { visibility: visible; } + + &:before { + visibility: visible; + } } } & .close-icon { position: absolute; left: 3px; - top: 6px; - color: black; - font-weight: bold; - font-size: 12px; - font-family: sans-serif; - vertical-align: top; + top: 2px; + cursor: pointer; + font-size: 8px; - &:before{ - content: "x"; - } + // icon glyph used from bootstrap visibility: hidden; } diff --git a/src/app/annotationViewer/annotationViewer.js b/src/app/annotationViewer/annotationViewer.js index 9a57c0da..61172001 100644 --- a/src/app/annotationViewer/annotationViewer.js +++ b/src/app/annotationViewer/annotationViewer.js @@ -11,7 +11,19 @@ avModule.controller('AnnotationViewerCtrl', ['$scope', '$element', '$attrs', '$t * @param Tag */ function AnnotationViewerCtrl($scope, $element, $attrs, $transclude, Tag) { - $scope.getTag = function getTag(id) { + $scope.getTag = function getTag(annotation) { + + // which tag to show + // HACK: show first only + var taggings = annotation.taggings; + + if (!taggings || taggings.length === 0) { + return ""; + } + + var tag = taggings ? taggings[0] : undefined; + var id = tag ? tag.id : undefined; + var tagObject = Tag.resolve(id); if (tagObject) { return tagObject.text; diff --git a/src/app/annotationViewer/annotationViewer.tpl.html b/src/app/annotationViewer/annotationViewer.tpl.html index bdc86390..6fde91bd 100644 --- a/src/app/annotationViewer/annotationViewer.tpl.html +++ b/src/app/annotationViewer/annotationViewer.tpl.html @@ -5,9 +5,9 @@
diff --git a/src/app/listen/_listen.scss b/src/app/listen/_listen.scss index a50845ac..00137882 100644 --- a/src/app/listen/_listen.scss +++ b/src/app/listen/_listen.scss @@ -29,40 +29,56 @@ } #timeline { + line-height: normal; + font-size: smaller; & * { border: solid black 1px; } - div { - clear: both; - } - - > div:nth-child(1) { - float: left; + & div { clear: none; - - div { - height: 25%; - } + display: inline-block; + margin: 0; + padding: 0; + text-align: center; } - > div:nth-child(2) { - overflow: hidden; - clear: none; + > div { + width: 100%; + + > div { + width: 100%; - div { - text-align: center; - span { - display: inline-block; - width: 33.1%; + > div { + width: 33%; - margin:0; + > div { + width: 32%; + > div { + width: 32%; + } + } } } } + #audioRecording { + display: block; + + & div:nth-child(1) { + width: 44%; + } + & div:nth-child(2) { + width: 11%; + } + & div:nth-child(3) { + width: 44%; + } + } + + } \ No newline at end of file diff --git a/src/app/listen/listen.js b/src/app/listen/listen.js index bd5546b6..271edd94 100644 --- a/src/app/listen/listen.js +++ b/src/app/listen/listen.js @@ -40,8 +40,8 @@ angular.module('bawApp.listen', []) } $scope.errorState = - !(baw.isNumber($routeParams.recordingId) - && baw.parseInt($routeParams.recordingId) >= 0); + !(baw.isNumber($routeParams.recordingId) && + baw.parseInt($routeParams.recordingId) >= 0); if ($scope.errorState) { console.warn("Invalid (or no) audio recording id specified in route... page rendering disabled"); @@ -160,7 +160,7 @@ angular.module('bawApp.listen', []) // TODO : map tag's $scope.model.audioEvents = - tempEvents.map(baw.Annotation.new); + tempEvents.map(baw.Annotation.create); }, function audioEventQueryFailure() { console.error("retrieval of audio events failed"); @@ -281,12 +281,12 @@ angular.module('bawApp.listen', []) //$scope.model.selectedAudioEvents.length = 0; angular.forEach($scope.model.audioEvents, function (value, key) { - value._selected = false; + value.selected = false; }); }; $scope.selectedFilter = function (audioEvent) { - return audioEvent._selected; + return audioEvent.selected; }; $scope.select2Settings = { @@ -339,7 +339,7 @@ angular.module('bawApp.listen', []) //var a = $scope.model.selectedAudioEvents[0]; //TODO: BROKEN! var a = $scope.model.audioEvents.filter(function (value) { - return value._selected === true; + return value.selected === true; })[0]; // prep tags diff --git a/src/app/listen/listen.tpl.html b/src/app/listen/listen.tpl.html index 1a9485d9..a09cfd49 100644 --- a/src/app/listen/listen.tpl.html +++ b/src/app/listen/listen.tpl.html @@ -6,36 +6,28 @@

Timeline

- -
-
Playlist
-
AudioRecording
-
Segment
-
Chunk
-
-
-
- 3 items previous - Current Segment - 2 items next -
-
- 112 minutes before - Current Segment - 1100 minutes after -
-
- 33 chunks previous - Current Chunk - 16 chunks next +
+
+
before
+
current
+
after
-
-   - Time index -   +
+
pli
+
+
segment
+
+
chunk
+
+ You are here +
+
chunk
+
+
segment
+
+
pli
-

Stats

    @@ -162,9 +154,9 @@

    Annotations

    - - - SELECTED:{{ae._selected}} + + + SELECTED:{{ae.selected}} diff --git a/src/common/jquery.drawabox.js b/src/common/jquery.drawabox.js index 22faf21e..79680b0e 100644 --- a/src/common/jquery.drawabox.js +++ b/src/common/jquery.drawabox.js @@ -123,13 +123,16 @@ if (contextData === undefined) { throw "Context data must be given"; } - var closeIconTemplate = ''; + var closeIconTemplate = ''; - var uniqueId = uniqueId || (-1 * Number.Unique()); + uniqueId = uniqueId || (-1 * Number.Unique()); $('.boxItem').attr(SELECTED_ATTRIBUTE, false); var newId = "boxItem_" + uniqueId; - contextData.currentMouseDragBoxId = newId; + + if (!silent) { + contextData.currentMouseDragBoxId = newId; + } if (contextData.options.showOnly === true) { closeIconTemplate = ""; @@ -172,6 +175,7 @@ // add other events $newBox.resizable({ handles: "all", + //containment: "parent", resize: function (event, ui) { contextData.options.boxResizing($newBox); }, stop: function (event, ui) { contextData.options.boxResized($newBox); } }); @@ -461,7 +465,7 @@ var elements = this.querySelectorAll(".boxItem[data-id='" + id.toString() + "']"); - if (elements.length == 0) { + if (elements.length === 0) { result[index] = false; } else if (elements.length > 1) { @@ -482,7 +486,7 @@ var $this = $(this); - if (this.querySelectorAll(".boxItem[data-id='" + id.toString() + "']").length != 0) { + if (this.querySelectorAll(".boxItem[data-id='" + id.toString() + "']").length !== 0) { throw "An element with that id already exists, cannot insert"; } diff --git a/src/components/directives/bawAnnotationViewer.js b/src/components/directives/bawAnnotationViewer.js index 57b0fcf9..1f1549b1 100644 --- a/src/components/directives/bawAnnotationViewer.js +++ b/src/components/directives/bawAnnotationViewer.js @@ -7,7 +7,7 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { return Math.abs(fraction - 1); } - function unitConversions(sampleRate, window, imageWidth, imageHeight) { + function calculateUnitConversions(sampleRate, window, imageWidth, imageHeight) { if (sampleRate === undefined || window === undefined || !imageWidth || !imageHeight) { console.warn("not enough information to calculate unit conversions"); return { pixelsPerSecond: NaN, pixelsPerHertz: NaN}; @@ -35,14 +35,15 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { console.warn("the image width does not3 conform well with the meta data"); } - console.info("unit update calculated successfully") - return { pixelsPerSecond: spectrogramPps, pixelsPerHertz: imagePph, nyquistFrequency: nyquistFrequency }; + console.info("unit update calculated successfully"); + return { pixelsPerSecond: spectrogramPps, pixelsPerHertz: imagePph, nyquistFrequency: nyquistFrequency, imageHeight: imageHeight }; } function updateUnitConversions(scope, imageWidth, imageHeight) { var conversions = {}; + // TODO: calculate unit-conversions without image media if (scope.model.media && scope.model.media.spectrogram) { - conversions = unitConversions(scope.model.media.sampleRate, scope.model.media.spectrogram.window, + conversions = calculateUnitConversions(scope.model.media.sampleRate, scope.model.media.spectrogram.window, imageWidth, imageHeight); } @@ -68,22 +69,20 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { }, invertHertz: function invertHertz(hertz) { return Math.abs(conversions.nyquistFrequency - hertz); + }, + invertPixels: function invertPixels(pixels) { + return Math.abs(conversions.imageHeight - pixels); } }; } - /** - * - * @param audioEvent - * @param box - * @param scope - */ function resizeOrMove(audioEvent, box, scope) { var boxId = baw.parseInt(box.id); if (audioEvent.__temporaryId__ === boxId) { - audioEvent.startTimeSeconds = scope.model.converters.pixelsToSeconds(box.left || 0); audioEvent.highFrequencyHertz = scope.model.converters.invertHertz(scope.model.converters.pixelsToHertz(box.top || 0)); + audioEvent.startTimeSeconds = scope.model.converters.pixelsToSeconds(box.left || 0); + audioEvent.endTimeSeconds = audioEvent.startTimeSeconds + scope.model.converters.pixelsToSeconds(box.width || 0); audioEvent.lowFrequencyHertz = audioEvent.highFrequencyHertz - scope.model.converters.pixelsToHertz(box.height || 0); @@ -141,15 +140,15 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { return; } - console.log("audioEvent watcher fired", value.__temporaryId__, value._selected); + console.log("audioEvent watcher fired", value.__temporaryId__, value.selected); // TODO: SET UP CONVERSIONS HERE - var top = scope.model.converters.hertzToPixels(value.highFrequencyHertz), + var top = scope.model.converters.invertPixels(scope.model.converters.hertzToPixels(value.highFrequencyHertz)), left = scope.model.converters.secondsToPixels(value.startTimeSeconds), width = scope.model.converters.secondsToPixels(value.endTimeSeconds - value.startTimeSeconds), height = scope.model.converters.hertzToPixels(value.highFrequencyHertz - value.lowFrequencyHertz); - drawaboxInstance.drawabox('setBox', value.__temporaryId__, top, left, height, width, value._selected); + drawaboxInstance.drawabox('setBox', value.__temporaryId__, top, left, height, width, value.selected); } }; @@ -252,15 +251,19 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { } scope.model.converters = updateUnitConversions(scope, scope.$image.width(), scope.$image.height()); - // redraw all boxes already drawn + // redraw all boxes already drawn (hacky way to force angular to see these objects as dirty!) scope.model.audioEvents.forEach(function (value) { - value.forceDodgyUpdate = (value.forceDodgyUpdate || 0) + 1 + value.forceDodgyUpdate = (value._forceDodgyUpdate || 0) + 1; }); } scope.$watch('model.media.spectrogram.url', updateConverters); - scope.$image[0].addEventListener('load', updateConverters, false); + scope.$image[0].addEventListener('load', function () { + scope.$apply(function () { + updateConverters(); + }); + }, false); updateConverters(); // init drawabox @@ -291,11 +294,11 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { // support for multiple selections - remove the clear // TODO: this is a very inefficient method of achieving this result angular.forEach(scope.model.audioEvents, function (value, key) { - value._selected = false; + value.selected = false; }); // new form of selecting - scope.model.audioEvents[element[0].annotationViewerIndex]._selected = true; + scope.model.audioEvents[element[0].annotationViewerIndex].selected = true; }); }, "boxResizing": function (element, box) { @@ -323,6 +326,9 @@ bawds.directive('bawAnnotationViewer', [ 'conf.paths', function (paths) { var itemToDelete = scope.model.audioEvents[element[0].annotationViewerIndex]; itemToDelete.deletedAt = (new Date()); + // TODO: delete index bound watcher... do not change array layout, keep it sparse + + // if (scope.model.selectedAudioEvents.length > 0) { // var index = scope.model.selectedAudioEvents.indexOf(itemToDelete); // diff --git a/src/components/models/annotation.js b/src/components/models/annotation.js index e77dceab..c48c6642 100644 --- a/src/components/models/annotation.js +++ b/src/components/models/annotation.js @@ -18,8 +18,12 @@ baw.Annotation = function Annotation(localIdOrResource, audioRecordingId) { throw new Error("Constructor called as a function"); } - this.__temporaryId__ = localId || (Number.Unique() * -1); - this._selected = false; + this.__temporaryId__ = localId || resource.id; //(Number.Unique() * -1); + if (!angular.isNumber(this.__temporaryId__)) { + throw "Is in an annotation is not a number!"; + } + + this.selected = false; this.audioEventTags = []; if (localId) { @@ -76,10 +80,10 @@ baw.Annotation = function Annotation(localIdOrResource, audioRecordingId) { this.toJSON = function () { return { id: this.id || this.__temporaryId__ - } + }; }; }; -baw.Annotation.new = function(arg) { +baw.Annotation.create = function(arg) { return new baw.Annotation(arg); }; diff --git a/src/components/services/services.js b/src/components/services/services.js index caeb35f5..995d9a38 100644 --- a/src/components/services/services.js +++ b/src/components/services/services.js @@ -173,8 +173,9 @@ for (key in obj) { if (obj.hasOwnProperty(key)) { // console.log(key); - if (key == propName) + if (key == propName) { return obj[key]; + } } } console.log('did not find match', propName, obj);