--- a/web/res/metadataplayer/LdtPlayer-core.js Fri Dec 11 18:11:13 2015 +0100
+++ b/web/res/metadataplayer/LdtPlayer-core.js Tue Dec 29 13:25:14 2015 +0100
@@ -13,7 +13,7 @@
|_| |___/
* Copyright 2010-2012 Institut de recherche et d'innovation
- * contributor(s) : Karim Hamidou, Samuel Huron, Raphael Velt, Thibaut Cavalie
+ * contributor(s) : Karim Hamidou, Samuel Huron, Raphael Velt, Thibaut Cavalie, Yves-Marie Haussonne, Nicolas Durand, Olivier Aubert
*
* contact@iri.centrepompidou.fr
* http://www.iri.centrepompidou.fr
@@ -28,6 +28,7 @@
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
+// Metadataplayer - version 0.2
/* Initialization of the namespace */
if (typeof window.IriSP === "undefined") {
@@ -82,7 +83,7 @@
var list = [],
positions = [],
text = _text.replace(/(^\s+|\s+$)/g,'');
-
+
function addToList(_rx, _startHtml, _endHtml) {
while(true) {
var result = _rx.exec(text);
@@ -101,11 +102,11 @@
positions.push(end);
}
}
-
+
if (_regexp) {
addToList(_regexp, '<span class="Ldt-Highlight">', '</span>');
}
-
+
addToList(/(https?:\/\/)?[\w\d\-]+\.[\w\d\-]+\S+/gm, function(matches) {
return '<a href="' + (matches[1] ? '' : 'http://') + matches[0] + '" target="_blank">';
}, '</a>');
@@ -114,19 +115,19 @@
}, '</a>');
addToList(/\*[^*]+\*/gm, '<b>', '</b>');
addToList(/[\n\r]+/gm, '', '<br />');
-
+
IriSP._(_extend).each(function(x) {
addToList.apply(null, x);
});
-
+
positions = IriSP._(positions)
.chain()
.uniq()
.sortBy(function(p) { return parseInt(p); })
.value();
-
+
var res = "", lastIndex = 0;
-
+
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
res += text.substring(lastIndex, pos);
@@ -144,11 +145,11 @@
}
lastIndex = pos;
}
-
+
res += text.substring(lastIndex);
-
+
return res;
-
+
};
IriSP.log = function() {
@@ -161,11 +162,27 @@
jqSel.attr("draggable", "true").on("dragstart", function(_event) {
var d = (typeof data === "function" ? data.call(this) : data);
try {
+ if (d.html === undefined && d.uri && d.text) {
+ d.html = '<a href="' + d.uri + '">' + d.text + '</a>';
+ }
IriSP._(d).each(function(v, k) {
- if (v) {
+ if (v && k != 'text' && k != 'html') {
_event.originalEvent.dataTransfer.setData("text/x-iri-" + k, v);
}
});
+ if (d.uri && d.text) {
+ _event.originalEvent.dataTransfer.setData("text/x-moz-url", d.uri + "\n" + d.text.replace("\n", " "));
+ _event.originalEvent.dataTransfer.setData("text/plain", d.text + " " + d.uri);
+ }
+ // Define generic text/html and text/plain last (least
+ // specific types, see
+ // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#Drag_Data)
+ if (d.html !== undefined) {
+ _event.originalEvent.dataTransfer.setData("text/html", d.html);
+ }
+ if (d.text !== undefined && ! d.uri) {
+ _event.originalEvent.dataTransfer.setData("text/plain", d.text);
+ }
} catch(err) {
_event.originalEvent.dataTransfer.setData("Text", JSON.stringify(d));
}
@@ -180,6 +197,62 @@
});
};
+IriSP.timestamp2ms = function(t) {
+ // Convert timestamp to numeric value
+ // It accepts the following forms:
+ // [h:mm:ss] [mm:ss] [ss]
+ var s = t.split(":").reverse();
+ while (s.length < 3) {
+ s.push("0");
+ }
+ return 1000 * (3600 * parseInt(s[2], 10) + 60 * parseInt(s[1], 10) + parseInt(s[0], 10));
+};
+
+IriSP.setFullScreen= function(elem, value) {
+ // Set fullscreen on or off
+ if (value) {
+ if (elem.requestFullscreen) {
+ elem.requestFullscreen();
+ } else if (elem.mozRequestFullScreen) {
+ elem.mozRequestFullScreen();
+ } else if (elem.webkitRequestFullscreen) {
+ elem.webkitRequestFullscreen();
+ } else if (elem.msRequestFullscreen) {
+ elem.msRequestFullscreen();
+ }
+ } else {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ }
+ }
+};
+
+IriSP.isFullscreen = function() {
+ return (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
+};
+
+IriSP.getFullscreenElement = function () {
+ return (document.fullscreenElement
+ || document.webkitFullscreenElement
+ || document.mozFullScreenElement
+ || document.msFullscreenElement
+ || undefined);
+};
+
+IriSP.getFullscreenEventname = function () {
+ return ((document.exitFullscreen && "fullscreenchange")
+ || (document.webkitExitFullscreen && "webkitfullscreenchange")
+ || (document.mozExitFullScreen && "mozfullscreenchange")
+ || (document.msExitFullscreen && "msfullscreenchange")
+ || "");
+};
+
/* js is where data is stored in a standard form, whatever the serializer */
//TODO: Separate Project-specific data from Source
@@ -757,6 +830,7 @@
this.volume = .5;
this.paused = true;
this.muted = false;
+ this.timeRange = false;
this.loadedMetadata = false;
var _this = this;
this.on("play", function() {
@@ -781,6 +855,18 @@
_a.trigger("enter");
_this.trigger("enter-annotation",_a);
});
+
+ if (_this.getTimeRange()){
+ if (_this.getTimeRange()[0] > _time) {
+ _this.pause();
+ _this.setCurrentTime(_this.getTimeRange()[0]);
+ }
+ if (_this.getTimeRange()[1] < _time){
+ _this.pause();
+ _this.setCurrentTime(_this.getTimeRange()[1]);
+ }
+ }
+
});
this.on("loadedmetadata", function() {
_this.loadedMetadata = true;
@@ -805,6 +891,10 @@
return this.muted;
};
+Playable.prototype.getTimeRange = function() {
+ return this.timeRange;
+}
+
Playable.prototype.setCurrentTime = function(_time) {
this.trigger("setcurrenttime",_time);
};
@@ -817,6 +907,16 @@
this.trigger("setmuted",_muted);
};
+Playable.prototype.setTimeRange = function(_timeBegin, _timeEnd) {
+ if ((_timeBegin < _timeEnd)&&(_timeBegin >= 0)&&(_timeEnd>0)){
+ return this.trigger("settimerange", [_timeBegin, _timeEnd]);
+ }
+}
+
+Playable.prototype.resetTimeRange = function() {
+ return this.trigger("resettimerange");
+}
+
Playable.prototype.play = function() {
this.trigger("setplay");
};
@@ -840,6 +940,17 @@
};
extendPrototype(Media, Playable);
+/* */
+
+var Media = Model.Media = function(_id, _source) {
+ Playable.call(this, _id, _source);
+ this.elementType = 'media';
+ this.duration = new Time();
+ this.video = '';
+ var _this = this;
+};
+
+extendPrototype(Media, Playable);
/* Default functions to be overriden by players */
@@ -905,6 +1016,19 @@
extendPrototype(Annotation, BaseElement);
+/* Set begin and end in one go, to avoid undesired side-effects in
+ * setBegin/setEnd interaction */
+Annotation.prototype.setBeginEnd = function(_beginMs, _endMs) {
+ _beginMs = Math.max(0,_beginMs);
+ _endMs = Math.max(0,_endMs);
+ if (_endMs < _beginMs)
+ _endMs = _beginMs;
+ this.begin.setMilliseconds(_beginMs);
+ this.end.setMilliseconds(_endMs);
+ this.trigger("change-begin");
+ this.trigger("change-end");
+};
+
Annotation.prototype.setBegin = function(_beginMs) {
this.begin.setMilliseconds(Math.max(0,_beginMs));
this.trigger("change-begin");
@@ -1432,7 +1556,17 @@
videoEl.append(_srcNode);
}
}
-
+ if (opts.subtitle) {
+ var _trackNode = IriSP.jQuery('<track>');
+ _trackNode.attr({
+ label: "Subtitles",
+ kind: "subtitles",
+ srclang: "fr",
+ src: opts.subtitle,
+ default: ""
+ });
+ videoEl.append(_trackNode);
+ }
jqselector.html(videoEl);
var mediaEl = videoEl[0];
@@ -1464,6 +1598,20 @@
}
});
+ media.on("settimerange", function(_timeRange){
+ media.timeRange = _timeRange;
+ try {
+ if (media.getCurrentTime() > _timeRange[0] || media.getCurrentTime() < _timeRange){
+ mediaEl.currentTime = (_timeRange[0] / 1000);
+ }
+ } catch (err) {
+ }
+ })
+
+ media.on("resettimerange", function(){
+ media.timeRange = false;
+ })
+
media.on("setplay", function() {
try {
mediaEl.play();
@@ -1517,7 +1665,13 @@
media.trigger("seeked");
});
-
+ videoEl.on("click", function() {
+ if (mediaEl.paused) {
+ media.play();
+ } else {
+ media.pause();
+ };
+ });
};
/* START contentapi-serializer.js */
@@ -1694,7 +1848,7 @@
if (typeof _data.content.img !== "undefined" && _data.content.img.src !== "undefined") {
_res.thumbnail = _data.content.img.src;
}
- _res.created = IriSP.Model.isoToDate(_data.meta["dc:created"]);
+ _res.created = IriSP.Model.isoToDate(_data.created ? _data.created : _data.meta? _data.meta["dc:created"] : "");
if (typeof _data.color !== "undefined") {
var _c = parseInt(_data.color).toString(16);
while (_c.length < 6) {
@@ -1906,20 +2060,26 @@
serializeAnnotation : function(_data, _source) {
var _annType = _data.getAnnotationType();
return {
+ id: _data.id,
begin: _data.begin.milliseconds,
end: _data.end.milliseconds,
content: {
+ data: (_data.content ? _data.content.data || {} : {}),
description: _data.description,
title: _data.title,
audio: _data.audio
},
+ id: _data.id ? _data.id : "", // If annotation is new, id will be undefined
tags: _data.getTagTexts(),
media: _data.getMedia().id,
+ project: _data.project_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
+ creator: _data.creator,
+ modified: _data.modified,
+ contributor: _data.contributor
}
};
},
@@ -1950,11 +2110,13 @@
return _tag.id;
});
_ann.setTags(_tagIds);
- _ann.setBegin(_anndata.begin);
- _ann.setEnd(_anndata.end);
+ _ann.setBeginEnd(_anndata.begin, _anndata.end);
if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
_ann.audio = _anndata.content.audio;
- }
+ };
+ if (_anndata.content.data) {
+ _ann.content = { data: _anndata.content.data };
+ };
_source.getAnnotations().push(_ann);
},
serialize : function(_source) {
@@ -1964,7 +2126,7 @@
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));
@@ -1972,7 +2134,8 @@
}
};
-/* End ldt_annotate serializer *//* ldt_localstorage serializer: Used to store personal annotations in local storage */
+/* End ldt_annotate serializer */
+/* ldt_localstorage serializer: Used to store personal annotations in local storage */
if (typeof IriSP.serializers === "undefined") {
IriSP.serializers = {};
@@ -1982,9 +2145,11 @@
serializeAnnotation : function(_data, _source) {
var _annType = _data.getAnnotationType();
return {
+ id: _data.id,
begin: _data.begin.milliseconds,
end: _data.end.milliseconds,
content: {
+ data: (_data.content ? _data.content.data || {} : {}),
description: _data.description,
title: _data.title,
audio: _data.audio
@@ -1995,7 +2160,9 @@
type: ( typeof _annType.dont_send_id !== "undefined" && _annType.dont_send_id ? "" : _annType.id ),
meta: {
created: _data.created,
- creator: _data.creator
+ creator: _data.creator,
+ modified: _data.modified,
+ contributor: _data.contributor
}
};
},
@@ -2005,6 +2172,8 @@
_ann.title = _anndata.content.title || "";
_ann.creator = _anndata.meta.creator || "";
_ann.created = new Date(_anndata.meta.created);
+ _ann.contributor = _anndata.meta.contributor || "";
+ _ann.modified = new Date(_anndata.meta.modified);
_ann.setMedia(_anndata.media, _source);
var _anntype = _source.getElement(_anndata.type);
if (!_anntype) {
@@ -2026,11 +2195,13 @@
return _tag.id;
});
_ann.setTags(_tagIds);
- _ann.setBegin(_anndata.begin);
- _ann.setEnd(_anndata.end);
+ _ann.setBeginEnd(_anndata.begin, _anndata.end);
if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
_ann.audio = _anndata.content.audio;
- }
+ };
+ if (_anndata.content.data) {
+ _ann.content = { data: _anndata.content.data };
+ };
_source.getAnnotations().push(_ann);
},
serialize : function(_source) {
@@ -2115,8 +2286,8 @@
backboneRelational: "backbone-relational.js",
paper: "paper.js",
jqueryMousewheel: "jquery.mousewheel.min.js",
- splitter: "jquery.splitter.js",
- cssSplitter: "jquery.splitter.css",
+ splitter: "jquery.touchsplitter.js",
+ cssSplitter: "jquery.touchsplitter.css",
renkanPublish: "renkan.js",
processing: "processing-1.3.6.min.js",
recordMicSwf: "record_mic.swf",
@@ -2206,7 +2377,7 @@
};
IriSP.guiDefaults = {
- width : 640,
+ width : 640,
container : 'LdtPlayer',
spacer_div_height : 0,
widgets: []
@@ -2258,46 +2429,45 @@
Metadataplayer.prototype.loadLibs = function() {
ns.log("IriSP.Metadataplayer.prototype.loadLibs");
var $L = $LAB
- .script(ns.getLib("Mustache"));
-
+ .queueScript(ns.getLib("Mustache"));
formerJQuery = !!window.jQuery;
former$ = !!window.$;
formerUnderscore = !!window._;
-
+
if (typeof ns.jQuery === "undefined") {
- $L.script(ns.getLib("jQuery"));
- }
-
- if (typeof ns._ === "undefined") {
- $L.script(ns.getLib("underscore"));
+ $L.queueScript(ns.getLib("jQuery"));
}
-
- if (typeof window.JSON == "undefined") {
- $L.script(ns.getLib("json"));
+
+ if (typeof ns._ === "undefined") {
+ $L.queueScript(ns.getLib("underscore"));
}
-
- $L.wait()
- .script(ns.getLib("jQueryUI"));
+
+ if (typeof window.JSON == "undefined") {
+ $L.queueScript(ns.getLib("json"));
+ }
+ $L.queueWait().queueScript(ns.getLib("jQueryUI")).queueWait();
/* widget specific requirements */
for(var _i = 0; _i < this.config.widgets.length; _i++) {
var _t = this.config.widgets[_i].type;
if (typeof ns.widgetsRequirements[_t] !== "undefined" && typeof ns.widgetsRequirements[_t].requires !== "undefined" ) {
for (var _j = 0; _j < ns.widgetsRequirements[_t].requires.length; _j++) {
- $L.script(ns.getLib(ns.widgetsRequirements[_t].requires[_j]));
+ $L.queueScript(ns.getLib(ns.widgetsRequirements[_t].requires[_j]));
}
}
}
-
+
var _this = this;
-
- $L.wait(function() {
+ $L.queueWait(function() {
_this.onLibsLoaded();
});
+
+ $L.runQueue();
};
Metadataplayer.prototype.onLibsLoaded = function() {
ns.log("IriSP.Metadataplayer.prototype.onLibsLoaded");
+
if (typeof ns.jQuery === "undefined" && typeof window.jQuery !== "undefined") {
ns.jQuery = window.jQuery;
if (former$ || formerJQuery) {
@@ -2310,9 +2480,10 @@
_.noConflict();
}
}
+
ns.loadCss(ns.getLib("cssjQueryUI"));
ns.loadCss(this.config.css);
-
+
this.$ = ns.jQuery('#' + this.config.container);
this.$.css({
"width": this.config.width,
@@ -2321,7 +2492,7 @@
if (typeof this.config.height !== "undefined") {
this.$.css("height", this.config.height);
}
-
+
this.widgets = [];
var _this = this;
ns._(this.config.widgets).each(function(widgetconf, key) {
@@ -2334,9 +2505,9 @@
});
});
this.$.find('.Ldt-Loader').detach();
-
+
this.widgetsLoaded = false;
-
+
this.on("widget-loaded", function() {
if (_this.widgetsLoaded) {
return;
@@ -2348,7 +2519,44 @@
_this.widgetsLoaded = true;
_this.trigger("widgets-loaded");
}
- });
+ });
+};
+
+Metadataplayer.prototype.loadLocalAnnotations = function(localsourceidentifier) {
+ if (this.localSource === undefined)
+ this.localSource = this.sourceManager.newLocalSource({serializer: IriSP.serializers['ldt_localstorage']});
+ // Load current local annotations
+ if (localsourceidentifier) {
+ // Allow to override localsourceidentifier when necessary (usually at init time)
+ this.localSource.identifier = localsourceidentifier;
+ }
+ this.localSource.deSerialize(window.localStorage[this.localSource.identifier] || "[]");
+ return this.localSource;
+};
+
+Metadataplayer.prototype.saveLocalAnnotations = function() {
+ // Save annotations back to localstorage
+ window.localStorage[this.localSource.identifier] = this.localSource.serialize();
+ // Merge modifications into widget source
+ // this.source.merge(this.localSource);
+};
+
+Metadataplayer.prototype.addLocalAnnotation = function(a) {
+ this.loadLocalAnnotations();
+ this.localSource.getAnnotations().push(a);
+ this.saveLocalAnnotations();
+};
+
+Metadataplayer.prototype.deleteLocalAnnotation = function(ident) {
+ this.localSource.getAnnotations().removeId(ident, true);
+ this.saveLocalAnnotations();
+};
+
+Metadataplayer.prototype.getLocalAnnotation = function (ident) {
+ this.loadLocalAnnotations();
+ // We cannot use .getElement since it fetches
+ // elements from the global Directory
+ return IriSP._.first(IriSP._.filter(this.localSource.getAnnotations(), function (a) { return a.id == ident; }));
};
Metadataplayer.prototype.loadMetadata = function(_metadataInfo) {
@@ -2371,9 +2579,9 @@
var _divs = this.layoutDivs(_widgetConfig.type);
_widgetConfig.container = _divs[0];
}
-
+
var _this = this;
-
+
if (typeof ns.Widgets[_widgetConfig.type] !== "undefined") {
ns._.defer(function() {
_callback(new ns.Widgets[_widgetConfig.type](_this, _widgetConfig));
@@ -2418,7 +2626,7 @@
if (typeof _height !== "undefined") {
divHtml.css("height", _height);
}
-
+
this.$.append(divHtml);
this.$.append(spacerHtml);
@@ -2427,7 +2635,8 @@
})(IriSP);
-/* End of widgets-container/metadataplayer.js *//* widgetsDefinition of an ancestor for the Widget classes */
+/* End of widgets-container/metadataplayer.js */
+/* widgetsDefinition of an ancestor for the Widget classes */
if (typeof IriSP.Widgets === "undefined") {
IriSP.Widgets = {};
@@ -2446,44 +2655,44 @@
IriSP.Widgets.Widget = function(player, config) {
-
+
if( typeof player === "undefined") {
/* Probably an abstract call of the class when
* individual widgets set their prototype */
return;
}
-
+
this.__subwidgets = [];
-
+
/* Setting all the configuration options */
var _type = config.type || "(unknown)",
_config = IriSP._.defaults({}, config, (player && player.config ? player.config.default_options : {}), this.defaults),
_this = this;
-
+
IriSP._(_config).forEach(function(_value, _key) {
_this[_key] = _value;
});
-
+
this.$ = IriSP.jQuery('#' + this.container);
-
+
if (typeof this.width === "undefined") {
this.width = this.$.width();
} else {
this.$.css("width", this.width);
}
-
+
if (typeof this.height !== "undefined") {
this.$.css("height", this.height);
}
-
+
/* Setting this.player at the end in case it's been overriden
* by a configuration option of the same name :-(
*/
this.player = player || new IriSP.FakeClass(["on","trigger","off","loadWidget","loadMetadata"]);
-
+
/* Adding classes and html attributes */
this.$.addClass("Ldt-TraceMe Ldt-Widget").attr("widget-type", _type);
-
+
this.l10n = (
typeof this.messages[IriSP.language] !== "undefined"
? this.messages[IriSP.language]
@@ -2493,10 +2702,14 @@
: this.messages["en"]
)
);
-
+
/* Loading Metadata if required */
-
+
function onsourceloaded() {
+ if (_this.localannotations) {
+ _this.localsource = player.loadLocalAnnotations(_this.localannotations);
+ _this.source.merge(_this.localsource);
+ }
if (_this.media_id) {
_this.media = this.getElement(_this.media_id);
} else {
@@ -2505,15 +2718,18 @@
};
_this.media = _this.source.getCurrentMedia(_mediaopts);
}
-
- _this.draw();
+ if (_this.pre_draw_callback){
+ IriSP.jQuery.when(_this.pre_draw_callback()).done(_this.draw());
+ }
+ else {
+ _this.draw();
+ }
_this.player.trigger("widget-loaded");
}
-
+
if (this.metadata) {
/* Getting metadata */
this.source = player.loadMetadata(this.metadata);
-
/* Call draw when loaded */
this.source.onLoad(onsourceloaded);
} else {
@@ -2521,8 +2737,8 @@
onsourceloaded();
}
}
-
-
+
+
};
IriSP.Widgets.Widget.prototype.defaults = {};
@@ -2640,7 +2856,7 @@
// 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) {
+ var annotations = _this.getWidgetAnnotations().sortBy(function(_annotation) {
return _annotation.begin;
});
for (var i = 0; i < annotations.length; i++) {
@@ -2653,6 +2869,51 @@
};
};
+/*
+ * Propose an export of the widget's annotations
+ *
+ * Parameter: a list of annotations. If not specified, the widget's annotations will be exported.
+ */
+IriSP.Widgets.Widget.prototype.exportAnnotations = function(annotations) {
+ var widget = this;
+ if (annotations === undefined)
+ annotations = this.getWidgetAnnotations();
+ var $ = IriSP.jQuery;
+
+ // FIXME: this should belong to a proper serialize/deserialize component?
+ var content = Mustache.to_html("[video:{{url}}]\n", {url: widget.media.url}) + annotations.map( function(a) { return Mustache.to_html("[{{ a.begin }}]{{ a.title }} {{ a.description }}[{{ a.end }}]", { a: a }); }).join("\n");
+
+ var el = $("<pre>")
+ .addClass("exportContainer")
+ .text(content)
+ .dialog({
+ title: "Annotation export",
+ open: function( event, ui ) {
+ // Select text
+ var range;
+ if (document.selection) {
+ range = document.body.createTextRange();
+ range.moveToElementText(this[0]);
+ range.select();
+ } else if (window.getSelection) {
+ range = document.createRange();
+ range.selectNode(this[0]);
+ window.getSelection().addRange(range);
+ }
+ },
+ autoOpen: true,
+ width: '80%',
+ minHeight: '400',
+ height: 400,
+ buttons: [ { text: "Close", click: function() { $( this ).dialog( "close" ); } },
+ { text: "Download", click: function () {
+ a = document.createElement('a');
+ a.setAttribute('href', 'data:text/plain;base64,' + btoa(content));
+ a.setAttribute('download', 'Annotations - ' + widget.media.title.replace(/[^ \w]/g, '') + '.txt');
+ a.click();
+ } } ]
+ });
+};
/**
* This method responsible of drawing a widget on screen.