--- a/sbin/build/compil.sh Fri Feb 13 16:48:05 2015 +0100
+++ b/sbin/build/compil.sh Fri Feb 13 16:57:53 2015 +0100
@@ -1,2 +1,3 @@
#!/bin/sh
-sh ../res/ant/bin/ant -f client.xml
+DIR=$(dirname $0)
+sh ${DIR}/../res/ant/bin/ant -f ${DIR}/client.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/libs/jquery.splitter.css Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,57 @@
+/*!
+ * StyleSheet for JQuery splitter Plugin
+ * Copyright (C) 2010 Jakub Jankiewicz <http://jcubic.pl>
+ *
+ * Same license as plugin
+ */
+.splitter_panel {
+ position: relative;
+}
+.splitter_panel .vsplitter {
+ background-color: grey;
+ cursor: col-resize;
+ z-index:900;
+ width: 4px;
+}
+
+.splitter_panel .hsplitter {
+ background-color: #5F5F5F;
+ cursor: row-resize;
+ z-index: 800;
+ height: 4px;
+}
+.splitter_panel .vsplitter.splitter-invisible,
+.splitter_panel .hsplitter.splitter-invisible {
+ background: none;
+}
+.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel,
+.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
+ position: absolute;
+ overflow: auto;
+}
+.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel {
+ height: 100%;
+}
+.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
+ width: 100%;
+}
+.splitter_panel .top_panel, .splitter_panel .left_panel, .splitter_panel .vsplitter {
+ top: 0;
+}
+.splitter_panel .top_panel, .splitter_panel .bottom_panel, .splitter_panel .left_panel, .splitter_panel .hsplitter {
+ left: 0;
+}
+.splitter_panel .bottom_panel {
+ bottom: 0;
+}
+.splitter_panel .right_panel {
+ right: 0;
+}
+.splitterMask {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1000;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/libs/jquery.splitter.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,278 @@
+/*!
+ * JQuery Spliter Plugin
+ * Copyright (C) 2010-2013 Jakub Jankiewicz <http://jcubic.pl>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+(function($, undefined) {
+ var count = 0;
+ var splitter_id = null;
+ var splitters = [];
+ var current_splitter = null;
+ $.fn.split = function(options) {
+ var data = this.data('splitter');
+ if (data) {
+ return data;
+ }
+ var panel_1;
+ var panel_2;
+ var settings = $.extend({
+ limit: 100,
+ orientation: 'horizontal',
+ position: '50%',
+ invisible: false,
+ onDragStart: $.noop,
+ onDragEnd: $.noop,
+ onDrag: $.noop
+ }, options || {});
+ this.settings = settings;
+ var cls;
+ var children = this.children();
+ if (settings.orientation == 'vertical') {
+ panel_1 = children.first().addClass('left_panel');
+ panel_2 = panel_1.next().addClass('right_panel');
+ cls = 'vsplitter';
+ } else if (settings.orientation == 'horizontal') {
+ panel_1 = children.first().addClass('top_panel')
+ panel_2 = panel_1.next().addClass('bottom_panel');
+ cls = 'hsplitter';
+ }
+ if (settings.invisible) {
+ cls += ' splitter-invisible';
+ }
+ var width = this.width();
+ var height = this.height();
+ var id = count++;
+ this.addClass('splitter_panel');
+ var splitter = $('<div/>').addClass(cls).mouseenter(function() {
+ splitter_id = id;
+ }).mouseleave(function() {
+ splitter_id = null;
+ }).insertAfter(panel_1);
+ var position;
+
+ function get_position(position) {
+ if (typeof position === 'number') {
+ return position;
+ } else if (typeof position === 'string') {
+ var match = position.match(/^([0-9]+)(px|%)$/);
+ if (match) {
+ if (match[2] == 'px') {
+ return +match[1];
+ } else {
+ if (settings.orientation == 'vertical') {
+ return (width * +match[1]) / 100;
+ } else if (settings.orientation == 'horizontal') {
+ return (height * +match[1]) / 100;
+ }
+ }
+ } else {
+ //throw position + ' is invalid value';
+ }
+ } else {
+ //throw 'position have invalid type';
+ }
+ }
+
+ var self = $.extend(this, {
+ refresh: function() {
+ var new_width = this.width();
+ var new_height = this.height();
+ if (width != new_width || height != new_height) {
+ width = this.width();
+ height = this.height();
+ self.position(position);
+ }
+ },
+ position: (function() {
+ if (settings.orientation == 'vertical') {
+ return function(n, silent) {
+ if (n === undefined) {
+ return position;
+ } else {
+ position = get_position(n);
+ var sw = splitter.width();
+ var sw2 = sw/2;
+ if (settings.invisible) {
+ var pw = panel_1.width(position).outerWidth();
+ panel_2.width(self.width()-pw);
+ splitter.css('left', pw-sw2);
+ } else {
+ var pw = panel_1.width(position-sw2).outerWidth();
+ panel_2.width(self.width()-pw-sw);
+ splitter.css('left', pw);
+ }
+ }
+ if (!silent) {
+ self.find('.splitter_panel').trigger('splitter.resize');
+ }
+ return self;
+ };
+ } else if (settings.orientation == 'horizontal') {
+ return function(n, silent) {
+ if (n === undefined) {
+ return position;
+ } else {
+ position = get_position(n);
+ var sw = splitter.height();
+ var sw2 = sw/2;
+ if (settings.invisible) {
+ var pw = panel_1.height(position).outerHeight();
+ panel_2.height(self.height()-pw);
+ splitter.css('top', pw-sw2);
+ } else {
+ var pw = panel_1.height(position-sw2).outerHeight();
+ panel_2.height(self.height()-pw-sw);
+ splitter.css('top', pw);
+ }
+ }
+ if (!silent) {
+ self.find('.splitter_panel').trigger('splitter.resize');
+ }
+ return self;
+ };
+ } else {
+ return $.noop;
+ }
+ })(),
+ orientation: settings.orientation,
+ limit: settings.limit,
+ isActive: function() {
+ return splitter_id === id;
+ },
+ destroy: function() {
+ self.removeClass('splitter_panel');
+ splitter.unbind('mouseenter');
+ splitter.unbind('mouseleave');
+ if (settings.orientation == 'vertical') {
+ panel_1.removeClass('left_panel');
+ panel_2.removeClass('right_panel');
+ } else if (settings.orientation == 'horizontal') {
+ panel_1.removeClass('top_panel');
+ panel_2.removeClass('bottom_panel');
+ }
+ self.unbind('splitter.resize');
+ self.find('.splitter_panel').trigger('splitter.resize');
+ splitters[id] = null;
+ splitter.remove();
+ var not_null = false;
+ for (var i=splitters.length; i--;) {
+ if (splitters[i] !== null) {
+ not_null = true;
+ break;
+ }
+ }
+ //remove document events when no splitters
+ if (!not_null) {
+ $(document.documentElement).unbind('.splitter');
+ $(window).unbind('resize.splitter');
+ self.data('splitter', null);
+ splitters = [];
+ count = 0;
+ }
+ }
+ });
+ self.bind('splitter.resize', function(e) {
+ var pos = self.position();
+ if (self.orientation == 'vertical' &&
+ pos > self.width()) {
+ pos = self.width() - self.limit-1;
+ } else if (self.orientation == 'horizontal' &&
+ pos > self.height()) {
+ pos = self.height() - self.limit-1;
+ }
+ if (pos < self.limit) {
+ pos = self.limit + 1;
+ }
+ self.position(pos, true);
+ });
+ //inital position of splitter
+ var pos;
+ if (settings.orientation == 'vertical') {
+ if (pos > width-settings.limit) {
+ pos = width-settings.limit;
+ } else {
+ pos = get_position(settings.position);
+ }
+ } else if (settings.orientation == 'horizontal') {
+ //position = height/2;
+ if (pos > height-settings.limit) {
+ pos = height-settings.limit;
+ } else {
+ pos = get_position(settings.position);
+ }
+ }
+ if (pos < settings.limit) {
+ pos = settings.limit;
+ }
+ self.position(pos, true);
+ if (splitters.length == 0) { // first time bind events to document
+ $(window).bind('resize.splitter', function() {
+ $.each(splitters, function(i, splitter) {
+ splitter.refresh();
+ });
+ });
+ $(document.documentElement).bind('mousedown.splitter', function(e) {
+ if (splitter_id !== null && e.which == 1) {
+ current_splitter = splitters[splitter_id];
+ $('<div class="splitterMask"></div>').css('cursor', splitter.css('cursor')).insertAfter(current_splitter);
+ current_splitter.settings.onDragStart(e);
+ return false;
+ }
+ }).bind('mouseup.splitter', function(e) {
+ if (current_splitter) {
+ $('.splitterMask').remove();
+ current_splitter.settings.onDragEnd(e);
+ current_splitter = null;
+ }
+ }).bind('mousemove.splitter', function(e) {
+ if (current_splitter !== null) {
+ var limit = current_splitter.limit;
+ var offset = current_splitter.offset();
+ if (current_splitter.orientation == 'vertical') {
+ var x = e.pageX - offset.left;
+ if (x <= current_splitter.limit) {
+ x = current_splitter.limit + 1;
+ } else if (x >= current_splitter.width() - limit) {
+ x = current_splitter.width() - limit - 1;
+ }
+ if (x > current_splitter.limit &&
+ x < current_splitter.width()-limit) {
+ current_splitter.position(x, true);
+ current_splitter.trigger('splitter.resize');
+ e.preventDefault();
+ }
+ } else if (current_splitter.orientation == 'horizontal') {
+ var y = e.pageY-offset.top;
+ if (y <= current_splitter.limit) {
+ y = current_splitter.limit + 1;
+ } else if (y >= current_splitter.height() - limit) {
+ y = current_splitter.height() - limit - 1;
+ }
+ if (y > current_splitter.limit &&
+ y < current_splitter.height()-limit) {
+ current_splitter.position(y, true);
+ current_splitter.trigger('splitter.resize');
+ e.preventDefault();
+ }
+ }
+ current_splitter.settings.onDrag(e);
+ }
+ });
+ }
+ splitters.push(self);
+ self.data('splitter', self);
+ return self;
+ };
+})(jQuery);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/libs/mousetrap-global-bind.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,36 @@
+/**
+ * adds a bindGlobal method to Mousetrap that allows you to
+ * bind specific keyboard shortcuts that will still work
+ * inside a text input field
+ *
+ * usage:
+ * Mousetrap.bindGlobal('ctrl+s', _saveChanges);
+ */
+/* global Mousetrap:true */
+Mousetrap = (function(Mousetrap) {
+ var _globalCallbacks = {},
+ _originalStopCallback = Mousetrap.stopCallback;
+
+ Mousetrap.stopCallback = function(e, element, combo, sequence) {
+ if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
+ return false;
+ }
+
+ return _originalStopCallback(e, element, combo);
+ };
+
+ Mousetrap.bindGlobal = function(keys, callback, action) {
+ Mousetrap.bind(keys, callback, action);
+
+ if (keys instanceof Array) {
+ for (var i = 0; i < keys.length; i++) {
+ _globalCallbacks[keys[i]] = true;
+ }
+ return;
+ }
+
+ _globalCallbacks[keys] = true;
+ };
+
+ return Mousetrap;
+}) (Mousetrap);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/libs/mousetrap.min.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,9 @@
+/* mousetrap v1.4.6 craig.is/killing/mice */
+(function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;g<l[a].length;++g)if(k=
+l[a][g],!(!c&&k.seq&&n[k.seq]!=k.level||h!=k.action||("keypress"!=h||d.metaKey||d.ctrlKey)&&b.sort().join(",")!==k.modifiers.sort().join(","))){var m=c&&k.seq==c&&k.level==v;(!c&&k.combo==e||m)&&l[a].splice(g,1);f.push(k)}return f}function K(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a,b,d,c){m.stopCallback(b,b.target||b.srcElement,d,c)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?
+b.stopPropagation():b.cancelBubble=!0)}function y(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:m.handleKey(b,K(a),a))}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function L(a,b,d,c){function e(b){return function(){u=b;++n[a];clearTimeout(D);D=setTimeout(t,1E3)}}function v(b){x(d,b,a);"keyup"!==c&&(z=A(b));setTimeout(t,10)}for(var g=n[a]=0;g<b.length;++g){var f=g+1===b.length?v:e(c||E(b[g+1]).action);F(b[g],f,c,a,g)}}function E(a,b){var d,
+c,e,f=[];d="+"===a?["+"]:a.split("+");for(e=0;e<d.length;++e)c=d[e],G[c]&&(c=G[c]),b&&"keypress"!=b&&H[c]&&(c=H[c],f.push("shift")),w(c)&&f.push(c);d=c;e=b;if(!e){if(!p){p={};for(var g in h)95<g&&112>g||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?L(a,f,b,d):(d=E(a,d),l[d.key]=l[d.key]||[],C(d.key,d.modifiers,{type:d.action},
+c,a,e),l[d.key][c?"unshift":"push"]({callback:b,modifiers:d.modifiers,action:d.action,seq:c,level:e,combo:a}))}var h={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},B={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},H={"~":"`","!":"1",
+"@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c<a.length;++c)F(a[c],b,d);return this},
+unbind:function(a,b){return m.bind(a,function(){},b)},trigger:function(a,b){if(q[a+":"+b])q[a+":"+b]({},a);return this},reset:function(){l={};q={};return this},stopCallback:function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},handleKey:function(a,b,d){var c=C(a,b,d),e;b={};var f=0,g=!1;for(e=0;e<c.length;++e)c[e].seq&&(f=Math.max(f,c[e].level));for(e=0;e<c.length;++e)c[e].seq?c[e].level==f&&(g=!0,
+b[c[e].seq]=1,x(c[e].callback,d,c[e].combo,c[e].seq)):g||x(c[e].callback,d,c[e].combo);c="keypress"==d.type&&I;d.type!=u||w(a)||c||t(b);I=g&&"keydown"==d.type}};J.Mousetrap=m;"function"===typeof define&&define.amd&&define(m)})(window,document);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/serializers/ldt_localstorage.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,79 @@
+/* ldt_localstorage serializer: Used to store personal annotations in local storage */
+
+if (typeof IriSP.serializers === "undefined") {
+ IriSP.serializers = {};
+}
+
+IriSP.serializers.ldt_localstorage = {
+ serializeAnnotation : function(_data, _source) {
+ var _annType = _data.getAnnotationType();
+ return {
+ begin: _data.begin.milliseconds,
+ end: _data.end.milliseconds,
+ content: {
+ description: _data.description,
+ title: _data.title,
+ audio: _data.audio
+ },
+ tags: _data.getTagTexts(),
+ media: _data.getMedia().id,
+ type_title: _annType.title,
+ type: ( typeof _annType.dont_send_id !== "undefined" && _annType.dont_send_id ? "" : _annType.id ),
+ meta: {
+ created: _data.created,
+ creator: _data.creator
+ }
+ };
+ },
+ deserializeAnnotation : function(_anndata, _source) {
+ var _ann = new IriSP.Model.Annotation(_anndata.id, _source);
+ _ann.description = _anndata.content.description || "";
+ _ann.title = _anndata.content.title || "";
+ _ann.creator = _anndata.meta.creator || "";
+ _ann.created = new Date(_anndata.meta.created);
+ _ann.setMedia(_anndata.media, _source);
+ var _anntype = _source.getElement(_anndata.type);
+ if (!_anntype) {
+ _anntype = new IriSP.Model.AnnotationType(_anndata.type, _source);
+ _anntype.title = _anndata.type_title;
+ _source.getAnnotationTypes().push(_anntype);
+ }
+ _ann.setAnnotationType(_anntype.id);
+ var _tagIds = IriSP._(_anndata.tags).map(function(_title) {
+ var _tags = _source.getTags(true).searchByTitle(_title, true);
+ if (_tags.length) {
+ var _tag = _tags[0];
+ }
+ else {
+ _tag = new IriSP.Model.Tag(_title.replace(/\W/g,'_'),_source);
+ _tag.title = _title;
+ _source.getTags().push(_tag);
+ }
+ return _tag.id;
+ });
+ _ann.setTags(_tagIds);
+ _ann.setBegin(_anndata.begin);
+ _ann.setEnd(_anndata.end);
+ if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
+ _ann.audio = _anndata.content.audio;
+ }
+ _source.getAnnotations().push(_ann);
+ },
+ serialize : function(_source) {
+ var _this = this;
+ return JSON.stringify(_source.getAnnotations().map(function (a) { return _this.serializeAnnotation(a, _source); }));
+ },
+ deSerialize : function(_data, _source) {
+ var _this = this;
+ if (typeof _data == "string") {
+ _data = JSON.parse(_data);
+ }
+
+ _source.addList('tag', new IriSP.Model.List(_source.directory));
+ _source.addList('annotationType', new IriSP.Model.List(_source.directory));
+ _source.addList('annotation', new IriSP.Model.List(_source.directory));
+ _data.map( function (a) { _this.deserializeAnnotation(a, _source); });
+ }
+};
+
+/* End ldt_localstorage serializer */
--- a/src/js/widgets-container/defaults.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/js/widgets-container/defaults.js Fri Feb 13 16:57:53 2015 +0100
@@ -23,9 +23,13 @@
backboneRelational: "backbone-relational.js",
paper: "paper.js",
jqueryMousewheel: "jquery.mousewheel.min.js",
+ splitter: "jquery.splitter.js",
+ cssSplitter: "jquery.splitter.css",
renkanPublish: "renkan.js",
processing: "processing-1.3.6.min.js",
- recordMicSwf: "record_mic.swf"
+ recordMicSwf: "record_mic.swf",
+ mousetrap: "mousetrap.min.js",
+ mousetrapGlobal: "mousetrap-global-bind.js"
},
locations : {
// use to define locations outside default_dir
@@ -100,6 +104,12 @@
},
MultiSegments: {
noCss: true
+ },
+ SlideVideoPlayer: {
+ requires: [ "jQuery", "jQueryUI", "splitter" ]
+ },
+ Shortcuts: {
+ requires: [ "mousetrap", "mousetrapGlobal" ]
}
};
--- a/src/js/widgets-container/widget.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/js/widgets-container/widget.js Fri Feb 13 16:57:53 2015 +0100
@@ -146,13 +146,19 @@
};
IriSP.Widgets.Widget.prototype.getWidgetAnnotations = function() {
+ var result = null;
if (typeof this.annotation_type === "undefined") {
- return this.media.getAnnotations();
+ result = this.media.getAnnotations();
+ } else if (this.annotation_type.elementType === "annotationType") {
+ result = this.annotation_type.getAnnotations();
+ } else {
+ result = this.media.getAnnotationsByTypeTitle(this.annotation_type);
}
- if (this.annotation_type.elementType === "annotationType") {
- return this.annotation_type.getAnnotations();
+ if (typeof this.annotation_filter !== "undefined") {
+ return this.annotation_filter(result);
+ } else {
+ return result;
}
- return this.media.getAnnotationsByTypeTitle(this.annotation_type);
};
IriSP.Widgets.Widget.prototype.getWidgetAnnotationsAtTime = function() {
@@ -196,9 +202,32 @@
});
};
+/*
+ * Position the player to the next/previous annotations based on current player position
+ *
+ * Parameter: offset: -1 for previous annotation, +1 for next annotation
+ */
+IriSP.Widgets.Widget.prototype.navigate = function(offset) {
+ // offset is normally either -1 (previous slide) or +1 (next slide)
+ var _this = this;
+ var currentTime = _this.media.getCurrentTime();
+ var annotations = _this.source.getAnnotations().sortBy(function(_annotation) {
+ return _annotation.begin;
+ });
+ for (var i = 0; i < annotations.length; i++) {
+ if (annotations[i].begin <= currentTime && currentTime < annotations[i].end) {
+ // Found a current annotation - clamp i+offset value to [0, length - 1]
+ i = Math.min(annotations.length - 1, Math.max(0, i + offset));
+ _this.media.setCurrentTime(annotations[i].begin);
+ break;
+ }
+ };
+};
+
+
/**
* This method responsible of drawing a widget on screen.
*/
IriSP.Widgets.Widget.prototype.draw = function() {
/* implemented by "sub-classes" */
-};
\ No newline at end of file
+};
--- a/src/widgets/Annotation.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Annotation.js Fri Feb 13 16:57:53 2015 +0100
@@ -41,9 +41,9 @@
+ '<h3 class="Ldt-Annotation-MashupOrigin Ldt-Annotation-HiddenWhenEmpty">{{l10n.excerpt_from}} <span class="Ldt-Annotation-MashupMedia"></span> <span class="Ldt-Annotation-Time Ldt-Annotation-HiddenWhenMinimized">'
+ '(<span class="Ldt-Annotation-MashupBegin"></span> - <span class="Ldt-Annotation-MashupEnd"></span>)</span></h3>'
+ '<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Creator-Block"><div class="Ldt-Annotation-Label">{{l10n.creator_}}</div>'
- + '{{#show_creator}}<p class="Ldt-Annotation-Labelled Ldt-Annotation-Creator"></p></div>'
- + '<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Description-Block"><div class="Ldt-Annotation-Label">{{l10n.description_}}</div>{{/show_creator}}'
- + '<p class="Ldt-Annotation-Labelled Ldt-Annotation-Description"></p></div>'
+ + '{{#show_creator}}<p class="Ldt-Annotation-Labelled Ldt-Annotation-Creator"></p></div>{{/show_creator}}'
+ + '{{#show_description}}<div class="Ldt-Annotation-Cleared Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Description-Block"><div class="Ldt-Annotation-Label">{{l10n.description_}}</div>'
+ + '<p class="Ldt-Annotation-Labelled Ldt-Annotation-Description"></p></div>{{/show_description}}'
+ '<div class="Ldt-Annotation-Tags-Block Ldt-Annotation-HiddenWhenMinimized Ldt-Annotation-HiddenWhenEmpty Ldt-Annotation-Cleared">'
+ '<div class="Ldt-Annotation-Label">{{l10n.tags_}}</div><ul class="Ldt-Annotation-Labelled Ldt-Annotation-Tags"></ul>'
+ '</div></div></div></div>';
@@ -53,6 +53,7 @@
start_minimized: false,
show_arrow : true,
show_creator: true,
+ show_description: true,
arrow_position: .5,
site_name : "Lignes de Temps",
search_on_tag_click: true,
@@ -231,4 +232,4 @@
IriSP.Widgets.Annotation.prototype.maximize = function() {
this.minimized = false;
this.$.find('.Ldt-Annotation-Inner').removeClass("Ldt-Annotation-Minimized");
-};
\ No newline at end of file
+};
--- a/src/widgets/AnnotationsList.css Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/AnnotationsList.css Fri Feb 13 16:57:53 2015 +0100
@@ -106,4 +106,12 @@
.Ldt-AnnotationsList-Play:hover {
background-position: 0 bottom;
-}
\ No newline at end of file
+}
+
+.Ldt-AnnotationsList-Control-Prev {
+ cursor: url(img/hand_left.png), pointer;
+}
+
+.Ldt-AnnotationsList-Control-Next {
+ cursor: url(img/hand_right.png), pointer;
+}
--- a/src/widgets/AnnotationsList.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/AnnotationsList.js Fri Feb 13 16:57:53 2015 +0100
@@ -29,6 +29,8 @@
limit_count : 20,
newest_first : false,
show_audio: true,
+ show_creator: false,
+ show_controls: false,
polemics : [{
keyword: "++",
background_color: "#c9ecc6"
@@ -58,15 +60,16 @@
IriSP.Widgets.AnnotationsList.prototype.template =
'<div class="Ldt-AnnotationsListWidget">'
+ '{{#show_audio}}<div class="Ldt-AnnotationsList-Audio"></div>{{/show_audio}}'
+ + '{{#show_controls}}<div class="Ldt-AnnotationsList-Controls"><span class="Ldt-AnnotationsList-Control-Prev">Previous</span> | <span class="Ldt-AnnotationsList-Control-Next">Next</span></div>{{/show_controls}}'
+ '<ul class="Ldt-AnnotationsList-ul">'
+ '</ul>'
+ '</div>';
IriSP.Widgets.AnnotationsList.prototype.annotationTemplate =
- '<li class="Ldt-AnnotationsList-li Ldt-TraceMe" trace-info="annotation-id:{{id}}, media-id:{{media_id}}" style="{{specific_style}}">'
+ '<li class="Ldt-AnnotationsList-li Ldt-Highlighter-Annotation Ldt-TraceMe" data-annotation="{{ id }}" data-begin="{{ begin_ms }}" data-end="{{ end_ms }}" trace-info="annotation-id:{{id}}, media-id:{{media_id}}" style="{{specific_style}}">'
+ '<div class="Ldt-AnnotationsList-ThumbContainer">'
+ '<a href="{{url}}" draggable="true">'
- + '<img class="Ldt-AnnotationsList-Thumbnail" src="{{thumbnail}}" />'
+ + '<img title="{{ begin }} - {{ atitle }}" class="Ldt-AnnotationsList-Thumbnail" src="{{thumbnail}}" />'
+ '</a>'
+ '</div>'
+ '<div class="Ldt-AnnotationsList-Duration">{{begin}} - {{end}}</div>'
@@ -187,7 +190,7 @@
_description = _annotation.description,
_thumbnail = (typeof _annotation.thumbnail !== "undefined" && _annotation.thumbnail ? _annotation.thumbnail : _this.default_thumbnail);
// Update : display creator
- if (_annotation.creator) {
+ if (_annotation.creator && _this.show_creator) {
_title = _annotation.creator;
}
if (_annotation.title) {
@@ -207,16 +210,23 @@
var _data = {
id : _annotation.id,
media_id : _annotation.getMedia().id,
+ atitle: IriSP.textFieldHtml(_annotation.title),
htitle : IriSP.textFieldHtml(_title),
hdescription : IriSP.textFieldHtml(_description),
begin : _annotation.begin.toString(),
end : _annotation.end.toString(),
+ begin_ms : _annotation.begin.milliseconds,
+ end_ms : _annotation.end.milliseconds,
thumbnail : _thumbnail,
url : _url,
tags : _annotation.getTagTexts(),
specific_style : (typeof _bgcolor !== "undefined" ? "background-color: " + _bgcolor : ""),
l10n: _this.l10n
};
+ if (_this.show_controls) {
+ _this.$.find(".Ldt-AnnotationsList-Control-Prev").on("click", function (e) { e.preventDefault(); _this.navigate(-1); });
+ _this.$.find(".Ldt-AnnotationsList-Control-Next").on("click", function (e) { e.preventDefault(); _this.navigate(+1); });
+ }
if (_this.show_audio && _annotation.audio && _annotation.audio.href && _annotation.audio.href != "null") {
_data.audio = true;
if (!_this.jwplayers[_annotation.id]) {
--- a/src/widgets/Controller.css Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Controller.css Fri Feb 13 16:57:53 2015 +0100
@@ -86,7 +86,7 @@
}
input.Ldt-Ctrl-SearchInput {
- width: 145px; height: 13px; margin: 2px; padding: 3px;
+ width: 145px; height: 20px; margin: 2px; padding: 3px;
border: 1px solid #8080a0; border-radius: 3px; font-size: 13px;
}
--- a/src/widgets/Controller.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Controller.js Fri Feb 13 16:57:53 2015 +0100
@@ -10,7 +10,8 @@
IriSP.Widgets.Controller.prototype.defaults = {
disable_annotate_btn: false,
disable_search_btn: false,
- disable_ctrl_f: false
+ disable_ctrl_f: false,
+ always_show_search: false
};
IriSP.Widgets.Controller.prototype.template =
@@ -27,7 +28,7 @@
+ '<div class="Ldt-Ctrl-spacer"></div>'
+ '{{/disable_search_btn}}'
+ '<div class="Ldt-Ctrl-Search">'
- + '<input class="Ldt-Ctrl-SearchInput Ldt-TraceMe"></input>'
+ + '<input placeholder="{{ l10n.search }}" type="search" class="Ldt-Ctrl-SearchInput Ldt-TraceMe"></input>'
+ '</div>'
+ '</div>'
+ '<div class="Ldt-Ctrl-Right">'
@@ -102,6 +103,7 @@
this.$.find(".Ldt-Ctrl-SearchBtn").click(this.functionWrapper("searchButtonHandler"));
this.$searchInput.keyup(this.functionWrapper("searchHandler"));
+ this.$searchInput.on("search", this.functionWrapper("searchHandler"));
var _volctrl = this.$.find(".Ldt-Ctrl-Volume-Control");
this.$.find('.Ldt-Ctrl-Sound')
@@ -168,7 +170,9 @@
annotations.on("search-cleared", function() {
_this.hideSearchBlock();
});
-
+ if (_this.always_show_search) {
+ _this.showSearchBlock();
+ }
};
/* Update the elasped time div */
@@ -237,7 +241,9 @@
};
IriSP.Widgets.Controller.prototype.hideSearchBlock = function() {
- this.$searchBlock.animate( { width: 0 }, 200);
+ if (! this.always_show_search) {
+ this.$searchBlock.animate( { width: 0 }, 200);
+ }
};
/** react to clicks on the search button */
--- a/src/widgets/CreateAnnotation.css Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/CreateAnnotation.css Fri Feb 13 16:57:53 2015 +0100
@@ -23,7 +23,7 @@
}
.Ldt-CreateAnnotation-Main {
- min-height: 150px;
+ min-height: 100px;
}
.Ldt-CreateAnnotation-Title, .Ldt-CreateAnnotation-Creator {
@@ -47,6 +47,7 @@
position: absolute;
bottom: 7px;
right: 7px;
+ display: inline;
color: #ffffff;
cursor: pointer;
background: url('img/submit_annotation.png');
@@ -163,4 +164,18 @@
a.Ldt-CreateAnnotation-Close:hover {
background-position: -17px 0;
-}
\ No newline at end of file
+}
+
+.Ldt-CreateAnnotation-Controls {
+ position: absolute;
+ top: 7px;
+ right: 7px;
+}
+
+span[class^='Ldt-CreateAnnotation-Control-'] {
+ background-color: #ddd;
+ padding: 2px;
+ margin: 4px;
+ border-radius: 3px;
+ border: 1px solid #888;
+}
--- a/src/widgets/CreateAnnotation.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/CreateAnnotation.js Fri Feb 13 16:57:53 2015 +0100
@@ -1,7 +1,16 @@
/* TODO: Add Social Network Sharing */
IriSP.Widgets.CreateAnnotation = function(player, config) {
+ var _this = this;
IriSP.Widgets.Widget.call(this, player, config);
+ if (_this.api_method == 'local' && window.localStorage[_this.api_endpoint_template]) {
+ this.source.onLoad(function () {
+ var _export = _this.player.sourceManager.newLocalSource({serializer: IriSP.serializers[_this.api_serializer]});
+ _export.deSerialize(window.localStorage[_this.api_endpoint_template]);
+ console.log("Loaded personal annotations", _export);
+ _this.source.merge(_export);
+ });
+ };
};
IriSP.Widgets.CreateAnnotation.prototype = new IriSP.Widgets.Widget();
@@ -12,6 +21,7 @@
start_visible : true,
always_visible : false,
show_slice : true,
+ show_controls: false,
show_arrow : true,
show_mic_record: false,
show_mic_play: false,
@@ -46,7 +56,8 @@
api_method: "POST",
after_send_timeout: 0,
close_after_send: false,
- tag_prefix: "#"
+ tag_prefix: "#",
+ slice_widget: null
};
IriSP.Widgets.CreateAnnotation.prototype.messages = {
@@ -104,8 +115,13 @@
+ '<span class="Ldt-CreateAnnotation-Times"> {{#show_slice}}{{l10n.from_time}} {{/show_slice}}{{^show_slice}}{{l10n.at_time}} {{/show_slice}} <span class="Ldt-CreateAnnotation-Begin">00:00</span>'
+ '{{#show_slice}} {{l10n.to_time}} <span class="Ldt-CreateAnnotation-End">{{end}}</span>{{/show_slice}}</span></span>'
+ '{{#show_creator_field}}{{l10n.your_name_}} <input class="Ldt-CreateAnnotation-Creator empty" value="{{creator_name}}" />{{/show_creator_field}}</h3>'
+ + '{{#show_controls}}<div class="Ldt-CreateAnnotation-Controls">'
+ + '<span class="Ldt-CreateAnnotation-Control-In">IN</span>'
+ + '<span class="Ldt-CreateAnnotation-Control-Out">OUT</span>'
+ + '<span class="Ldt-CreateAnnotation-Control-Play">Play</span>'
+ + '</div>{{/show_controls}}'
+ '<textarea class="Ldt-CreateAnnotation-Description empty" placeholder="{{l10n.type_description}}"></textarea>'
- + '<div class="Ldt-CreateAnnotation-Avatar"><img src="{{creator_avatar}}" title="{{creator_name}}"></img></div>'
+ + '{{#show_creator_field}}<div class="Ldt-CreateAnnotation-Avatar"><img src="{{creator_avatar}}" title="{{creator_name}}"></img></div>{{/show_creator_field}}'
+ '<input type="submit" class="Ldt-CreateAnnotation-Submit" value="{{l10n.submit}}" />'
+ '{{#show_mic_record}}<div class="Ldt-CreateAnnotation-RecBlock"><div class="Ldt-CreateAnnotation-RecLabel">Add voice annotation</div>'
+ ' <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="220" height="160">'
@@ -130,7 +146,7 @@
+ ' pluginspage="http://www.macromedia.com/go/getflashplayer">'
+ ' </embed>'
+ ' </object>'
- + '</div>{{/show_mic_record}}'
+ + '</div>{{/show_mic_record}}'
+ '{{#tags.length}}<div class="Ldt-CreateAnnotation-Tags"><div class="Ldt-CreateAnnotation-TagTitle">{{l10n.add_keywords_}}</div><ul class="Ldt-CreateAnnotation-TagList">'
+ '{{#tags}}<li class="Ldt-CreateAnnotation-TagLi" tag-id="{{id}}" data-text="{{tag_prefix}}{{title}}"><span class="Ldt-CreateAnnotation-TagButton">{{title}}</span></li>{{/tags}}</ul></div>{{/tags.length}}'
+ '{{#polemics.length}}<div class="Ldt-CreateAnnotation-Polemics"><div class="Ldt-CreateAnnotation-PolemicTitle">{{l10n.add_polemic_keywords_}}</div><ul class="Ldt-CreateAnnotation-PolemicList">'
@@ -140,15 +156,15 @@
+ '<div class="Ldt-CreateAnnotation-Screen Ldt-CreateAnnotation-Error">{{^always_visible}}<a title="{{l10n.close_widget}}" class="Ldt-CreateAnnotation-Close" href="#"></a>{{/always_visible}}<div class="Ldt-CreateAnnotation-InnerBox">{{l10n.error_while_contacting}}</div></div>'
+ '<div class="Ldt-CreateAnnotation-Screen Ldt-CreateAnnotation-Saved">{{^always_visible}}<a title="{{l10n.close_widget}}" class="Ldt-CreateAnnotation-Close" href="#"></a>{{/always_visible}}<div class="Ldt-CreateAnnotation-InnerBox">{{l10n.annotation_saved}}</div></div>'
+ '</div></div>';
-
+
IriSP.Widgets.CreateAnnotation.prototype.draw = function() {
var _this = this;
-
+
this.begin = new IriSP.Model.Time();
this.end = this.source.getDuration();
-
+
this.tag_prefix = this.tag_prefix || "";
-
+
if (this.tag_titles && !this.tags) {
if(!(this.tag_titles.length==1 && this.tag_titles[0]=="")){
this.tags = IriSP._(this.tag_titles).map(function(_tag_title) {
@@ -184,13 +200,13 @@
this.renderTemplate();
if (this.show_mic_record) {
this.recorder = this.$.find("embed")[0];
-
+
window.setAudioUrl = function(_url) {
_this.audio_url = _url;
- }
+ };
}
if (this.show_slice) {
- this.insertSubwidget(
+ this.slice_widget = this.insertSubwidget(
this.$.find(".Ldt-CreateAnnotation-Slice"),
{
type: "Slice",
@@ -243,14 +259,34 @@
if (this.show_creator_field) {
this.$.find(".Ldt-CreateAnnotation-Creator").bind("change keyup input paste", this.functionWrapper("onCreatorChange"));
}
-
+ this.$.find("[class^='Ldt-CreateAnnotation-Control-']").click(function() {
+ var action = this.className.replace('Ldt-CreateAnnotation-Control-', '');
+ switch (action) {
+ case "In":
+ // Set In bound to current player time
+ _this.begin = new IriSP.Model.Time(_this.media.getCurrentTime() || 0);
+ _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString());
+ break;
+ case "Out":
+ // Set In bound to current player time
+ _this.end = new IriSP.Model.Time(_this.media.getCurrentTime() || _this.media.duration);
+ _this.$.find(".Ldt-CreateAnnotation-End").html(_this.end.toString());
+ break;
+ case "Play":
+ _this.media.setCurrentTime(_this.begin);
+ _this.media.play()
+ break;
+ }
+ return false;
+ });
+
if (this.start_visible) {
this.show();
} else {
this.$.hide();
this.hide();
}
-
+
this.onMdpEvent("CreateAnnotation.toggle","toggle");
this.$.find("form").submit(this.functionWrapper("onSubmit"));
};
@@ -294,10 +330,18 @@
};
IriSP.Widgets.CreateAnnotation.prototype.toggle = function() {
+ var _this = this;
if (!this.always_visible) {
if (this.visible) {
this.hide();
} else {
+ _this.begin = new IriSP.Model.Time(_this.media.getCurrentTime() || 0);
+ _this.end = new IriSP.Model.Time(_this.media.getCurrentTime() || 0);
+ _this.$.find(".Ldt-CreateAnnotation-Begin").html(_this.begin.toString());
+ _this.$.find(".Ldt-CreateAnnotation-End").html(_this.end.toString());
+ if (_this.slice_widget) {
+ _this.slice_widget.setBounds(_this.begin, _this.end);
+ }
this.show();
}
}
@@ -375,11 +419,11 @@
if (!this.onDescriptionChange() || (this.show_title_field && !this.onTitleChange()) || (this.show_creator_field && !this.onCreatorChange())) {
return false;
}
-
+
if (this.recorder) {
this.recorder.stopRecord();
}
-
+
var _this = this,
_exportedAnnotations = new IriSP.Model.List(this.player.sourceManager), /* Création d'une liste d'annotations contenant une annotation afin de l'envoyer au serveur */
_export = this.player.sourceManager.newLocalSource({serializer: IriSP.serializers[this.api_serializer]}), /* Création d'un objet source utilisant un sérialiseur spécifique pour l'export */
@@ -387,7 +431,7 @@
_annotationTypes = this.source.getAnnotationTypes().searchByTitle(this.annotation_type, true), /* Récupération du type d'annotation dans lequel l'annotation doit être ajoutée */
_annotationType = (_annotationTypes.length ? _annotationTypes[0] : new IriSP.Model.AnnotationType(false, _export)), /* Si le Type d'Annotation n'existe pas, il est créé à la volée */
_url = Mustache.to_html(this.api_endpoint_template, {id: this.source.projectId}); /* Génération de l'URL à laquelle l'annotation doit être envoyée, qui doit inclure l'ID du projet */
-
+
/* Si nous avons dû générer un ID d'annotationType à la volée... */
if (!_annotationTypes.length) {
/* Il ne faudra pas envoyer l'ID généré au serveur */
@@ -395,7 +439,7 @@
/* Il faut inclure le titre dans le type d'annotation */
_annotationType.title = this.annotation_type;
}
-
+
/*
* Nous remplissons les données de l'annotation générée à la volée
* ATTENTION: Si nous sommes sur un MASHUP, ces éléments doivent se référer AU MEDIA D'ORIGINE
@@ -403,20 +447,22 @@
_annotation.setMedia(this.source.currentMedia.id); /* Id du média annoté */
_annotation.setBegin(this.begin); /*Timecode de début */
_annotation.setEnd(this.end); /* Timecode de fin */
-
+ _annotation.created = new Date(); /* Date de création de l'annotation */
+
_annotation.setAnnotationType(_annotationType.id); /* Id du type d'annotation */
+ _annotation.description = this.$.find(".Ldt-CreateAnnotation-Description").val(); /* Champ description */
if (this.show_title_field) {
/* Champ titre, seulement s'il est visible */
_annotation.title = this.$.find(".Ldt-CreateAnnotation-Title").val();
+ } else {
+ _annotation.title = _annotation.description;
}
- _annotation.created = new Date(); /* Date de création de l'annotation */
- _annotation.description = this.$.find(".Ldt-CreateAnnotation-Description").val(); /* Champ description */
-
+
var tagIds = Array.prototype.map.call(
this.$.find(".Ldt-CreateAnnotation-TagLi.selected"),
function(el) { return IriSP.jQuery(el).attr("tag-id")}
);
-
+
IriSP._(_annotation.description.match(/#[^\s#.,;]+/g)).each(function(_tt) {
var _tag,
_tag_title = _tt.replace(/^#/,''),
@@ -431,9 +477,9 @@
if (tagIds.indexOf(_tag.id) === -1) {
tagIds.push(_tag.id);
}
-
+
})
-
+
_annotation.setTags(IriSP._(tagIds).uniq()); /*Liste des ids de tags */
if (this.audio_url) {
_annotation.audio = {
@@ -448,47 +494,65 @@
_annotation.creator = this.creator_name;
}
_exportedAnnotations.push(_annotation); /* Ajout de l'annotation à la liste à exporter */
- _export.addList("annotation",_exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */
-
- var _this = this;
- /* Envoi de l'annotation via AJAX au serveur ! */
- IriSP.jQuery.ajax({
- url: _url,
- type: this.api_method,
- contentType: 'application/json',
- data: _export.serialize(), /* L'objet Source est sérialisé */
- success: function(_data) {
- _this.showScreen('Saved'); /* Si l'appel a fonctionné, on affiche l'écran "Annotation enregistrée" */
- if (_this.after_send_timeout) { /* Selon les options de configuration, on revient à l'écran principal ou on ferme le widget, ou rien */
- window.setTimeout(
- function() {
- _this.close_after_send
- ? _this.hide()
- : _this.show();
- },
- _this.after_send_timeout
- );
+
+ if (this.api_method == 'local') {
+ // Append to existing localStorage annotations
+ // FIXME: handle movie ids
+ /* Use localStorage */
+ /* Data will be serialized in the localStore property designated by api_endpoint_template */
+ _export.addList("annotation", _exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */
+ _this.source.merge(_export); /* On ajoute la nouvelle annotation au recueil original */
+ // Import previously saved local annotations
+ if (window.localStorage[_this.api_endpoint_template]) {
+ _export.deSerialize(window.localStorage[_this.api_endpoint_template]);
+ }
+ // Save everything back
+ window.localStorage[_this.api_endpoint_template] = _export.serialize();
+ if (_this.pause_on_write && _this.media.getPaused()) {
+ _this.media.play();
+ }
+ _this.player.trigger("AnnotationsList.refresh"); /* On force le rafraîchissement du widget AnnotationsList */
+ _this.$.find(".Ldt-CreateAnnotation-Description").val("");
+ } else {
+ _export.addList("annotation",_exportedAnnotations); /* Ajout de la liste à exporter à l'objet Source */
+ /* Envoi de l'annotation via AJAX au serveur ! */
+ IriSP.jQuery.ajax({
+ url: _url,
+ type: this.api_method,
+ contentType: 'application/json',
+ data: _export.serialize(), /* L'objet Source est sérialisé */
+ success: function(_data) {
+ _this.showScreen('Saved'); /* Si l'appel a fonctionné, on affiche l'écran "Annotation enregistrée" */
+ if (_this.after_send_timeout) { /* Selon les options de configuration, on revient à l'écran principal ou on ferme le widget, ou rien */
+ window.setTimeout(
+ function() {
+ _this.close_after_send
+ ? _this.hide()
+ : _this.show();
+ },
+ _this.after_send_timeout
+ );
+ }
+ _export.getAnnotations().removeElement(_annotation, true); /* Pour éviter les doublons, on supprime l'annotation qui a été envoyée */
+ _export.deSerialize(_data); /* On désérialise les données reçues pour les réinjecter */
+ _this.source.merge(_export); /* On récupère les données réimportées dans l'espace global des données */
+ if (_this.pause_on_write && _this.media.getPaused()) {
+ _this.media.play();
+ }
+ _this.player.trigger("AnnotationsList.refresh"); /* On force le rafraîchissement du widget AnnotationsList */
+ },
+ error: function(_xhr, _error, _thrown) {
+ IriSP.log("Error when sending annotation", _thrown);
+ _export.getAnnotations().removeElement(_annotation, true);
+ _this.showScreen('Error');
+ window.setTimeout(function(){
+ _this.showScreen("Main")
+ },
+ (_this.after_send_timeout || 5000));
}
- _export.getAnnotations().removeElement(_annotation, true); /* Pour éviter les doublons, on supprime l'annotation qui a été envoyée */
- _export.deSerialize(_data); /* On désérialise les données reçues pour les réinjecter */
- _this.source.merge(_export); /* On récupère les données réimportées dans l'espace global des données */
- if (_this.pause_on_write && _this.media.getPaused()) {
- _this.media.play();
- }
- _this.player.trigger("AnnotationsList.refresh"); /* On force le rafraîchissement du widget AnnotationsList */
- },
- error: function(_xhr, _error, _thrown) {
- IriSP.log("Error when sending annotation", _thrown);
- _export.getAnnotations().removeElement(_annotation, true);
- _this.showScreen('Error');
- window.setTimeout(function(){
- _this.showScreen("Main")
- },
- (_this.after_send_timeout || 5000));
- }
- });
- this.showScreen('Wait');
-
+ });
+ this.showScreen('Wait');
+ }
+
return false;
};
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/Highlighter.css Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,3 @@
+.currentAnnotation {
+ border: solid 3px red;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/Highlighter.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,43 @@
+IriSP.Widgets.Highlighter = function(player, config) {
+ var _this = this;
+ IriSP.Widgets.Widget.call(this, player, config);
+ this.throttledRefresh = IriSP._.throttle(function() {
+ console.log("highlighter Refresh");
+ _this.update();
+ }, 800);
+};
+
+/**
+ * Highlighter widget
+ * This widgets highlights the current annotations by setting the
+ * .activeAnnotation class on appropriate .Ldt-Highlighter-Annotation
+ * elements. These elements *must* have data-begin and data-end properties specifying their bounds (in ms) (and data-media specifying the media-id)
+ */
+IriSP.Widgets.Highlighter.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Highlighter.prototype.defaults = {
+}
+
+IriSP.Widgets.Highlighter.prototype.update = function() {
+ var _this = this;
+ var _currentTime = _this.media.getCurrentTime();
+ _this.$.find(".Ldt-Highlighter-Annotation", document).toggleClass("currentAnnotation", function () {
+ return (this.dataset.media === _this.media.id && this.dataset.begin <= _currentTime && _currentTime < this.dataset.end);
+ });
+ console.log(_this.$.find(".currentAnnotation"));
+ return false;
+};
+
+IriSP.Widgets.Highlighter.prototype.draw = function() {
+ var _this = this;
+
+ var _events = [
+ "timeupdate",
+ "seeked",
+ "loadedmetadata"
+ ];
+ for (var _i = 0; _i < _events.length; _i++) {
+ _this.onMediaEvent(_events[_i], _this.throttledRefresh);
+ }
+ _this.throttledRefresh();
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/ImageDisplay.css Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,32 @@
+/* Nothing */
+.Ldt-ImageDisplay-Container {
+ margin: auto;
+}
+
+.Ldt-ImageDisplay-Image {
+ max-width: 100%;
+ max-height: 100%;
+}
+
+.Ldt-ImageDisplay-Overlay {
+ width: 20%;
+ min-width: 20px;
+ height: 100%;
+ opacity: 0.1;
+ position: absolute;
+ top: 0px;
+ bottom: 0px;
+}
+
+.Ldt-ImageDisplay-Overlay:hover {
+}
+
+.Ldt-ImageDisplay-Overlay-Left {
+ left: 0px;
+ cursor: url(img/hand_left.png), pointer;
+}
+
+.Ldt-ImageDisplay-Overlay-Right {
+ right: 0px;
+ cursor: url(img/hand_right.png), pointer;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/ImageDisplay.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,42 @@
+/* This widget displays the image associated to the annotation in the given container */
+
+IriSP.Widgets.ImageDisplay = function(player, config) {
+ IriSP.Widgets.Widget.call(this, player, config);
+}
+
+IriSP.Widgets.ImageDisplay.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.ImageDisplay.prototype.defaults = {
+ annotation_type: "Slides",
+ // container: "imageContainer"
+}
+
+IriSP.Widgets.ImageDisplay.prototype.template = '<div class="Ldt-ImageDisplay-Container"><img class="Ldt-ImageDisplay-Image" title="" alt="Slide Image" src=""/><div class="Ldt-ImageDisplay-Overlay Ldt-ImageDisplay-Overlay-Left"></div><div class="Ldt-ImageDisplay-Overlay Ldt-ImageDisplay-Overlay-Right"></div></div>';
+
+IriSP.Widgets.ImageDisplay.prototype.annotationTemplate = '';
+
+IriSP.Widgets.ImageDisplay.prototype.update = function(annotation) {
+ // Update the widget with data corresponding to the annotation
+ this.image.setAttribute("title", IriSP.textFieldHtml(annotation.title) + " - " + annotation.begin.toString());
+ this.image.setAttribute("src", annotation.thumbnail);
+};
+
+IriSP.Widgets.ImageDisplay.prototype.draw = function() {
+ var _annotations = this.getWidgetAnnotations().sortBy(function(_annotation) {
+ return _annotation.begin;
+ });
+ var _this = this;
+ _this.renderTemplate();
+ _this.image = _this.$.find("img")[0];
+
+ _this.$.find(".Ldt-ImageDisplay-Overlay-Left").on("click", function () { _this.navigate(-1); });
+ _this.$.find(".Ldt-ImageDisplay-Overlay-Right").on("click", function () { _this.navigate(+1); });
+
+ _annotations.forEach(function(_a) {
+ _a.on("enter", function() {
+ _this.update(_a);
+ });
+ });
+ if (_annotations.length)
+ _this.update(_annotations[0]);
+}
--- a/src/widgets/MultiSegments.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/MultiSegments.js Fri Feb 13 16:57:53 2015 +0100
@@ -133,4 +133,4 @@
}
});*/
}
-};
\ No newline at end of file
+};
--- a/src/widgets/Segments.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Segments.js Fri Feb 13 16:57:53 2015 +0100
@@ -13,7 +13,9 @@
background: "#e0e0e0",
overlap: .25,
found_color: "#FF00FC",
- faded_found_color: "#ff80fc"
+ faded_found_color: "#ff80fc",
+ // Display creator info in segment tooltip
+ show_creator: true
};
IriSP.Widgets.Segments.prototype.template =
@@ -77,7 +79,7 @@
color : color,
medcolor: medcolor,
lowcolor: lowcolor,
- text: (_annotation.creator ? (_annotation.creator + " : ") : "" ) + _fulltext.replace(/(\n|\r|\r\n)/mg,' ').replace(/(^.{120,140})[\s].+$/m,'$1…'),
+ text: ((_this.show_creator && _annotation.creator) ? (_annotation.creator + " : ") : "" ) + _fulltext.replace(/(\n|\r|\r\n)/mg,' ').replace(/(^.{120,140})[\s].+$/m,'$1…'),
left : _left,
width : _width,
top: _top,
@@ -89,14 +91,17 @@
};
var _html = Mustache.to_html(_this.annotationTemplate, _data),
_el = IriSP.jQuery(_html);
- _el.mouseover(function() {
+ _el.mouseover(function() {
_annotation.trigger("select");
+ _this.player.trigger('annotation-select', _annotation);
})
.mouseout(function() {
_annotation.trigger("unselect");
+ _this.player.trigger('annotation-unselect', _annotation);
})
.click(function() {
_annotation.trigger("click");
+ _this.player.trigger('annotation-click', _annotation);
})
.appendTo(list_$);
IriSP.attachDndData(_el, {
@@ -172,4 +177,4 @@
this.$.find('.Ldt-Segments-Position').css({
left: _x + "px"
});
-};
\ No newline at end of file
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/Shortcuts.css Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,1 @@
+/* Empty file */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/Shortcuts.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,58 @@
+IriSP.Widgets.Shortcuts = function(player, config) {
+ IriSP.Widgets.Widget.call(this, player, config);
+};
+
+/**
+ * Keyboard shortcuts widget
+ * This widgets add global shortcuts for common actions.
+ * The default shortcuts are:
+ * - Escape or Control-space for play/pause
+ * - Control-left for rewind (+shift to go faster)
+ * - Control-right for forward (+shift to go faster)
+ */
+IriSP.Widgets.Shortcuts.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Shortcuts.prototype.defaults = {
+ // Time increment, in ms, for backward/forward navigation
+ time_increment: 2000
+}
+
+IriSP.Widgets.Shortcuts.prototype.draw = function() {
+ var _this = this;
+
+ /* Standard shortcuts */
+ Mousetrap.bindGlobal(["esc", "ctrl+space"], function (e) {
+ e.preventDefault();
+ if (! _this.media.getPaused()) {
+ _this.media.pause();
+ } else {
+ _this.media.play();
+ }
+ return false;
+ });
+ Mousetrap.bindGlobal("ctrl+left", function (e) {
+ // Backward
+ e.preventDefault();
+ _this.media.setCurrentTime(Math.max(0, _this.media.getCurrentTime() - _this.time_increment));
+ return false;
+ });
+ Mousetrap.bindGlobal("ctrl+shift+left", function (e) {
+ // Backward
+ e.preventDefault();
+ _this.media.setCurrentTime(Math.max(0, _this.media.getCurrentTime() - 5 * _this.time_increment));
+ return false;
+ });
+ Mousetrap.bindGlobal("ctrl+right", function (e) {
+ // Forward
+ e.preventDefault();
+ _this.media.setCurrentTime(Math.min(_this.media.duration, _this.media.getCurrentTime() + _this.time_increment));
+ return false;
+ });
+ Mousetrap.bindGlobal("ctrl+shift+right", function (e) {
+ // Forward
+ e.preventDefault();
+ _this.media.setCurrentTime(Math.min(_this.media.duration, _this.media.getCurrentTime() + 5 * _this.time_increment));
+ return false;
+ });
+
+};
--- a/src/widgets/Slice.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Slice.js Fri Feb 13 16:57:53 2015 +0100
@@ -67,9 +67,16 @@
this.getWidgetAnnotations().forEach(function(_a) {
_a.on("enter", function() {
- _this.$slider.slider("values",[_a.begin, _a.end]);
+ _this.setBounds(_a.begin, _a.end);
});
});
+ this.player.on("annotation-click", function(_a) {
+ _this.setBounds(_a.begin, _a.end);
+ });
+};
+
+IriSP.Widgets.Slice.prototype.setBounds = function(begin, end) {
+ this.$slider.slider("values", [ begin, end ]);
};
IriSP.Widgets.Slice.prototype.show = function() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/SlideVideoPlayer.css Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,23 @@
+/* Empty for now */
+
+.Ldt-SlideVideoPlayer-panel {
+ display: flex;
+ width: 50%;
+ float: left;
+}
+
+.Ldt-SlideVideoPlayer {
+ min-height: 400px;
+ max-height: 100%;
+}
+
+.Ldt-SlideVideoPlayer {
+ width: 100%;
+ max-width: 100%;
+ height: 100%;
+ max-height: 100%;
+}
+
+.Ldt-SlideVideoPlayer h2 {
+ display: none;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/SlideVideoPlayer.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,46 @@
+IriSP.Widgets.SlideVideoPlayer = function(player, config) {
+ IriSP.loadCss(IriSP.getLib("cssSplitter"));
+ IriSP.Widgets.Widget.call(this, player, config);
+};
+
+IriSP.Widgets.SlideVideoPlayer.prototype = new IriSP.Widgets.Widget();
+
+
+IriSP.Widgets.SlideVideoPlayer.prototype.defaults = {
+};
+
+IriSP.Widgets.SlideVideoPlayer.prototype.template = '<div class="Ldt-SlideVideoPlayer">\
+<div class="Ldt-SlideVideoPlayer-slide Ldt-SlideVideoPlayer-panel">\
+</div>\
+<div class="Ldt-SlideVideoPlayer-video Ldt-SlideVideoPlayer-panel"></div>\
+</div>';
+
+IriSP.Widgets.SlideVideoPlayer.prototype.draw = function() {
+ var _this = this;
+
+ _this.renderTemplate();
+ this.insertSubwidget(
+ _this.$.find(".Ldt-SlideVideoPlayer-panel.Ldt-SlideVideoPlayer-slide"),
+ {
+ type: "ImageDisplay",
+ annotation_type: _this.annotation_type,
+ width: '100%'
+ },
+ "slide"
+ );
+ this.insertSubwidget(
+ _this.$.find(".Ldt-SlideVideoPlayer-panel.Ldt-SlideVideoPlayer-video"),
+ {
+ type: "HtmlPlayer",
+ video: _this.video,
+ width: '100%',
+ url_transform: _this.url_transform
+ },
+ "player"
+ );
+ // FIXME: this should be better implemented through a signal sent
+ // when widgets are ready (and not just loaded)
+ window.setTimeout(function () {
+ _this.$.find(".Ldt-SlideVideoPlayer").split({ orientation: 'vertical' });
+ }, 1000);
+}
--- a/src/widgets/Trace.js Fri Feb 13 16:48:05 2015 +0100
+++ b/src/widgets/Trace.js Fri Feb 13 16:57:53 2015 +0100
@@ -68,9 +68,9 @@
this.tracer.trace("TraceWidgetInit", {});
-
+ _this.player.trigger("trace-ready");
this.mouseLocation = '';
- IriSP.jQuery(".Ldt-Widget").on("click mouseenter mouseleave", ".Ldt-TraceMe", function(_e) {
+ IriSP.jQuery(".Ldt-Widget").on("mousedown mouseenter mouseleave", ".Ldt-TraceMe", function(_e) {
var _target = IriSP.jQuery(this);
var _widget = _target.attr("widget-type") || _target.parents(".Ldt-Widget").attr("widget-type"),
@@ -127,4 +127,4 @@
if (this.js_console && typeof window.console !== "undefined" && typeof console.log !== "undefined") {
console.log("tracer.trace('" + _traceName + "', " + JSON.stringify(_arg) + ");");
}
-};
\ No newline at end of file
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/Transcript.js Fri Feb 13 16:57:53 2015 +0100
@@ -0,0 +1,37 @@
+/* This widget displays annotations as a transcript */
+
+IriSP.Widgets.Transcript = function(player, config) {
+ IriSP.Widgets.Widget.call(this, player, config);
+}
+
+IriSP.Widgets.Transcript.prototype = new IriSP.Widgets.Widget();
+
+IriSP.Widgets.Transcript.prototype.defaults = {
+ annotation_type: "Caption"
+ // container: "transcriptContainer"
+}
+
+IriSP.Widgets.Transcript.prototype.template = '<div class="Ldt-TranscriptWidget"></div>';
+
+IriSP.Widgets.Transcript.prototype.annotationTemplate = '<a data-begin="{{ begin }}" data-end="{{ end }}" data-id="{{ id }}" class="Ldt-Transcript-Annotation" href="#{{id}}">{{ content }}</a> ';
+
+IriSP.Widgets.Transcript.prototype.draw = function() {
+ var _annotations = this.getWidgetAnnotations();
+ var _this = this;
+ var content;
+
+ _this.renderTemplate();
+ content = _this.$.find('.Ldt-TranscriptWidget');
+
+ _annotations.forEach(function(_a) {
+ var _data = {
+ id : _a.id,
+ content : IriSP.textFieldHtml(_a.title),
+ begin : _a.begin.toString(),
+ end : _a.end.toString()
+ };
+ var _html = Mustache.to_html(_this.annotationTemplate, _data);
+ var _el = IriSP.jQuery(_html);
+ content.append(_el);
+ });
+};
Binary file src/widgets/img/hand_left.png has changed
Binary file src/widgets/img/hand_right.png has changed