/* Share Annotation Plugin v1.0 (https://github.com/danielcebrian/share-annotator) Copyright (C) 2014 Daniel Cebri�n Robles License: https://github.com/danielcebrian/share-annotator/blob/master/License.rst This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Generated by CoffeeScript 1.6.3 var _ref, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Annotator.Plugin.Share = (function(_super) { __extends(Share, _super); //Default Share configuration Share.prototype.options = { shareIn:['facebook','twitter','email','google'], getUrl:{ 'facebook':function(title,link,noteText){ return 'https://www.facebook.com/sharer/sharer.php?s=100&p[url]='+link+'&p[title]='+encodeURIComponent('Open Video Annotation')+'&p[summary]='+noteText; }, 'twitter':function(title,link,noteText){ return 'https://twitter.com/intent/tweet?original_referer='+link+'&source=tweetbutton&url='+link+ "&via=OpenVideoAnnotation&text=" +encodeURIComponent('I want to share the next Open Video Annotation: '); }, 'google':function(title,link,noteText){ return 'https://plus.google.com/share?url='+link; }, 'email': function(title,link,noteText){ return 'mailto:?subject='+title+'&body='+link; } }, baseUrl:'', //baseUrl = the base url for all the shared annotations }; function Share(element,options) { if (typeof options!='undefined') this.options.shareIn = typeof options.shareIn!='undefined'?options.shareIn:this.options.shareIn; this.buildHTMLShareButton = __bind(this.buildHTMLShareButton, this); this.runningAPI = __bind(this.runningAPI, this); this.updateViewer = __bind(this.updateViewer, this); _ref = Share.__super__.constructor.apply(this, arguments); return _ref; } Share.prototype.field = null; Share.prototype.input = null; Share.prototype.pluginInit = function() { console.log("Share-pluginInit"); //Check that annotator is working if (!Annotator.supported()) { return; } //-- Editor this.field = this.annotator.editor.addField({ type: 'input', //options (textarea,input,select,checkbox) }); //Modify the element created with annotator to be an invisible span var newfield = Annotator.$('<li class="annotator-item">'+this.buildHTMLShareButton('Share without saving:')+'</li>'); Annotator.$(this.field).replaceWith(newfield); this.field=newfield[0]; //Create the actions for the buttons this.buttonsActions(this.field,2,this.options.baseUrl); //2 is the method of the API that will be for share without saving //Init the API plugin var APIoptions = this.initAPI(); this.runAPI(APIoptions); //-- Viewer var newview = this.annotator.viewer.addField({ load: this.updateViewer, }); return this.input = $(this.field).find(':input'); }; //Share button HTML Share.prototype.buildHTMLShareButton = function(title,id) { var title = title || '', id = typeof id!='undefined'?'annotationId="'+id+'"':'', titleText = title!=''?'<div class="share-text-annotator">'+title+'</div>':'', shareButton = '<div class="share-button-annotator share-button" '+id+'></div>', popup = '<div class="share-popup-overlay-bg" style="z-index:30000000000"><div class="share-popup"><div class="share-popup-items"></div><div class="close-btn">Close</div></div></div>'; return '<div class="share-container-annotator">'+titleText+shareButton+popup+'</div>'; } //template for the design of the Share Plugin Share.prototype.buildHTMLPopup = function(title) { var buttons = ''; if (typeof this.options.shareIn!='undefined'){ this.options.shareIn.forEach(function(item) { buttons += '<div class="share-'+item+'-annotator share-button">'+item.charAt(0).toUpperCase() + item.slice(1)+'</div>'; }); } this.uri = typeof this.uri!='undefined'?this.uri:''; var title = '<div class="share-popup-title">'+title.replace(":","")+'</div>', copy = '<div class="share-popup-copy">Copy and Share:</div>', uri = '<input type="text" class="share-popup-uri" onclick="javascript:this.select();" readonly="true" value="'+this.uri+'">', popup = title + buttons + copy + uri; return popup; } //Create the actions for the buttons Share.prototype.buttonsActions = function(field,method,url) { var share = this; // hide popup when user clicks on close button $(field).find('.close-btn').click(function() { $('.share-popup-overlay-bg').hide(); }); // hides the popup if user clicks anywhere outside the container $(field).find('.share-popup-overlay-bg').click(function() { $('.share-popup-overlay-bg').hide(); }); // prevents the overlay from closing if user clicks inside the popup overlay $(field).find('.share-popup').click(function() { return false; }); // Share button $(field).find('.share-button-annotator.share-button').click(function() { event.preventDefault(); // disable normal link function so that it doesn't refresh the page var _field = this, ovaId = $(this).attr('annotationId'), title = method == 1?'Share':'Share without saving'; // share.uri will be useful for buildHTMLPopup functions share.uri = share.createAPIURL(method,ovaId,url); //display your popup $(this).parent().find('.share-popup-overlay-bg').show(); //build buttons $(this).parent().find('.share-popup-items').html(share.buildHTMLPopup(title)); //buttons actions if (typeof share.options.shareIn!='undefined'){ share.options.shareIn.forEach(function(item) { $(_field).parent().find('.share-'+item+'-annotator.share-button').click(function() { var url = share.createAPIURL(method,ovaId,url), title = "Sharing a annotation with Open Video Annotation"; link = encodeURIComponent(url), noteText = share.getSource('ovaText'), finalUrl = ''; if (method==1){ var viewer = share.annotator.viewer, textarea = $(viewer.element).find('div:first').html(); noteText = encodeURIComponent(textarea); } finalUrl = typeof share.options.getUrl[item]!='undefined'?share.options.getUrl[item](title,link,noteText):''; if(typeof share.options.getUrl[item]!='undefined') window.open(finalUrl); }); }); } }); }; Share.prototype.createAPIURL = function(method,ovaId,url) { var annotator = this.annotator, editor = annotator.editor, method = method || 1, //url = location.protocol + '//' + location.host + location.pathname, url = url || window.location.href; url += (url.indexOf('?') >= 0)?'&':'?'; if (method === 1){ var ovaId = typeof ovaId!='undefined'?ovaId:''; url += 'ovaId=' + ovaId; }else if (method === 2){ var ovaStart = this.getSource('ovaStart'), ovaEnd = this.getSource('ovaEnd'), ovaText = this.getSource('ovaText'); url += 'ovaStart='+ ovaStart +'&ovaEnd='+ ovaEnd +'&ovaText='+ ovaText; if(typeof editor.VideoJS!='undefined' && editor.VideoJS !== -1){//Video Annotation var ovaContainer = this.getSource('ovaContainer'), ovaSrc = this.getSource('ovaSrc'); url += '&ovaContainer='+ovaContainer +'&ovaSrc='+ ovaSrc; }else{//Text Annotation var ovastartOffset = this.getSource('ovastartOffset'), ovaendOffset = this.getSource('ovaendOffset'); url += '&ovastartOffset='+ovastartOffset +'&ovaendOffset='+ ovaendOffset; } } return url; }; Share.prototype.getSource = function(source) { var source = source || ''; if (source == 'ovaId') {//method 1 source=this.annotation.id; }else{//method 2 var annotator = this.annotator, editor = annotator.editor, textarea = $(editor.element).find('textarea')[0]; if(source == 'ovaText') source = textarea.value; if (typeof editor.VideoJS!='undefined' && editor.VideoJS !== -1){//Video Annotation if(source == 'ovaContainer') source = editor.VideoJS; else if(source == 'ovaSrc') source = annotator.mplayer[editor.VideoJS].tech.options_.source.src; else if(source == 'ovaStart') source = annotator.mplayer[editor.VideoJS].rangeslider.getValues().start; else if(source == 'ovaEnd') source = annotator.mplayer[editor.VideoJS].rangeslider.getValues().end; }else{//Text Annotation var annotation = editor.annotation; if(source == 'ovastartOffset') source = annotation.ranges[0].startOffset; else if(source == 'ovaendOffset') source = annotation.ranges[0].endOffset; else if(source == 'ovaStart') source = annotation.ranges[0].start; else if(source == 'ovaEnd') source = annotation.ranges[0].end; } } return encodeURIComponent(source); }; Share.prototype.initAPI = function() { console.log("initAPI"); // -- Detect API in the URL -- // /* The first option is to give a known id of an annotation Example http://url.com/#id=rTcpOjIMT2aF1apDtboC-Q */ var API = {}, ovaId = this.getParameterByName('ovaId'), //Method 1 (Obligatory) start = this.getParameterByName('ovaStart'), //Method 2 (Obligatory) end = this.getParameterByName('ovaEnd'), //Method 2 (Obligatory) container = this.getParameterByName('ovaContainer'), //Method 2 (Obligatory) src = this.getParameterByName('ovaSrc'),//Method 2 (Obligatory) text = this.getParameterByName('ovaText'),//Method 2 user = this.getParameterByName('ovaUser'),//Method 2 startOffset = this.getParameterByName('ovastartOffset'),//Method 2 endOffset = this.getParameterByName('ovaendOffset');//Method 2 //remove the variables from the url browser var stripped_url = top.location.href; if (ovaId != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaId'); if (start != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaStart'); if (end != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaEnd'); if (container != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaContainer'); if (src != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaSrc'); if (text != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaText'); if (user != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaUser'); if (startOffset != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovastartOffset'); if (endOffset != '') stripped_url = this.removeVariableFromURL(stripped_url, 'ovaendOffset'); window.history.pushState("object or string", "Title", stripped_url); // Method 1 API with the Id of the annotation //Example: http://danielcebrian.com/annotations/demo.html?&ovaId=wtva_SjnQb2HtqppDihKug if(ovaId != ''){ $.extend(API,{method:1,ovaId:ovaId}); } //Method 2 API with all the parameter to load the annotation //Example with video: http://danielcebrian.com/annotations/demo.html?ovaContainer=vid1&ovaSrc=http%3A%2F%2Fvideo-js.zencoder.com%2Foceans-clip.mp4&ovaStart=2&ovaEnd=10&ovaText=This%20is%20test&ovaUser=Test%20User //Example with text: http://danielcebrian.com/annotations/demo.html?ovaStart=%2Fp%5B1%5D&ovaEnd=%2Fp%5B1%5D&ovastartOffset=542&ovaendOffset=572&ovaText=API if(start!='' && end!='' && container!='' && src!=''){//video api $.extend(API,{method:2,start:start,end:end,container:container,src:src,text:text,user:user}); }else if(start!='' && end!='' && startOffset!='' && endOffset!=''){//text api $.extend(API,{method:2,start:start,end:end,startOffset:startOffset,endOffset:endOffset,text:text,user:user}); } return API; } Share.prototype.runningAPI = function (annotations,API){ console.log("runningAPI"); var wrapper = $('.annotator-wrapper').parent()[0], mplayer, osda, self=this; //Set Annotator in wrapper to fix quick DOM $.data(wrapper, 'annotator', self.annotator);//Set the object in the span annotator = window.annotator = $.data(wrapper, 'annotator'); mplayer = typeof annotator.mplayer!='undefined'?annotator.mplayer:[]; osda = typeof annotator.osda!='undefined'?annotator.osda:[]; //Detect if the URL has an API element if (typeof API!='undefined' && typeof API.method!='undefined' && (API.method=='1'||API.method=='2')) { if(API.method=='1'){ var allannotations = annotator.plugins['Store'].annotations, ovaId = decodeURIComponent(API.ovaId); for (var item in allannotations) { var an = allannotations[item]; if (typeof an.id!='undefined' && an.id == ovaId){//this is the annotation if(self._isVideo(an)){//It is a video if (typeof mplayer[an.target.container]!='undefined'){ var player = mplayer[an.target.container]; if (player.id_ == an.target.container){ var anFound = an; videojs(player.id_).ready(function(){ if (player.techName != 'Youtube'){ player.preload('auto'); } player.autoPlayAPI = anFound; player.play(); }); } } }else if(self._isVideo(an)){//It is a OpenSeaDragon Annotation if (typeof mplayer[an.target.container]!='undefined'){ var player = mplayer[an.target.container]; if (player.id_ == an.target.container){ var anFound = an; videojs(player.id_).ready(function(){ if (player.techName != 'Youtube'){ player.preload('auto'); } player.autoPlayAPI = anFound; player.play(); }); } } }else{//It is a text var hasRanges = typeof an.ranges!='undefined' && typeof an.ranges[0] !='undefined', startOffset = hasRanges?an.ranges[0].startOffset:'', endOffset = hasRanges?an.ranges[0].endOffset:''; if(typeof startOffset!='undefined' && typeof endOffset!='undefined'){ //change the color $(an.highlights).addClass('api'); //animate to the annotation $('html,body').animate({ scrollTop: $(an.highlights[0]).offset().top}, 'slow'); } } } } }else if (API.method=='2'){ if (typeof mplayer!='undefined'){ //variable for Video var container = decodeURIComponent(API.container), player = mplayer[container], isVideo = (typeof player!='undefined' && container==player.id_), isNumber = (!isNaN(parseFloat(API.start)) && isFinite(API.start) && !isNaN(parseFloat(API.end)) && isFinite(API.end)), isSource = false; if(isVideo){ //Compare without extension var src = decodeURIComponent(API.src), targetSrc = src.substring(0,src.lastIndexOf(".")), playerSrc = player.tech.options_.source.src==''?player.tag.currentSrc:player.tech.options_.source.src; playerSrc = playerSrc.substring(0,playerSrc.lastIndexOf(".")) isSource = (targetSrc == playerSrc); } //Open Video Annotation if(isVideo && isNumber && isSource){ var annotation = { rangeTime: { start:API.start, end:API.end }, created: new Date().toISOString(), updated: new Date().toISOString(), target:{ container: container, src: src }, media: 'video', text:decodeURIComponent(API.text), user:decodeURIComponent(API.user) }; videojs(player.id_).ready(function(){ if (player.techName != 'Youtube'){ player.preload('auto'); } player.autoPlayAPI = annotation; player.play(); }); } } //variable for text var startOffset = API.startOffset, endOffset = API.endOffset; //Text Annotation if(!isVideo && typeof startOffset!='undefined' && typeof endOffset!='undefined'){ var annotation = { ranges: [{ start:decodeURIComponent(API.start), end:decodeURIComponent(API.end), startOffset:decodeURIComponent(API.startOffset), endOffset:decodeURIComponent(API.endOffset), }], created: new Date().toISOString(), updated: new Date().toISOString(), media: 'text', text:decodeURIComponent(API.text), user:decodeURIComponent(API.user) }; //show the annotation annotator.setupAnnotation(annotation); //to change the color $(annotation.highlights).addClass('api'); //animate to the annotation $('html,body').animate({ scrollTop: $(annotation.highlights[0]).offset().top}, 'slow'); } } } //Let know to others API that this plugin is loaded annotator.isShareLoaded = true; annotator.publish('shareloaded'); } Share.prototype.runAPI = function(API) { var self = this; var func = function (annotations){ self.runningAPI(annotations,API); self.annotator.unsubscribe("annotationsLoaded",func); }; this.annotator //-- Finished the Annotator DOM .subscribe("annotationsLoaded",func); } Share.prototype._isVideo = function(an){ //Detect if the annotation is a Open Video Annotation var an = an || {} rt = an.rangeTime, isVideo = (typeof an.media!='undefined' && an.media=='video'), hasContainer = (typeof an.target!='undefined' && typeof an.target.container!='undefined' ), isNumber = (typeof rt!='undefined' && !isNaN(parseFloat(rt.start)) && isFinite(rt.start) && !isNaN(parseFloat(rt.end)) && isFinite(rt.end)); return (isVideo && hasContainer && isNumber); } Share.prototype.getParameterByName = function(name) { name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), //results = regex.exec(location.search), results = regex.exec('?'+window.location.href.split('?')[1]); return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); }; Share.prototype.removeVariableFromURL = function(url_string, variable_name) { var URL = String(url_string); var regex = new RegExp( "\\?" + variable_name + "=[^&]*&?", "gi"); URL = URL.replace(regex,'?'); regex = new RegExp( "\\&" + variable_name + "=[^&]*&?", "gi"); URL = URL.replace(regex,'&'); URL = URL.replace(/(\?|&)$/,''); regex = null; return URL; } Share.prototype.updateViewer = function(field, annotation) { this.annotation = annotation; var self = this, field = $(field), ret = field.addClass('share-viewer-annotator').html(function() { var string; return self.buildHTMLShareButton('Share:',self.getSource('ovaId')); }); //Create the actions for the buttons this.buttonsActions(field[0],1,this.options.baseUrl); //1 is the method of the API that will be for share some annotation in the database return ret; }; return Share; })(Annotator.Plugin);