You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4570 lines
228 KiB
4570 lines
228 KiB
if(!self.bigshot){bigshot={};bigshot.Object={extend:function(b,c){for(var a in c.prototype){if(b.prototype[a]){b.prototype[a]._super=c.prototype[a]}else{b.prototype[a]=c.prototype[a]}}},resolve:function(b){var e=b.split(".");var a=self;for(var d=0;d<e.length;++d){a=a[e[d]]}return a},validate:function(a,b){},alertr:function(b){var c="";for(var a in b){c+=a+":"+b[a]+"\n"}alert(c)},logr:function(b){var c="";for(var a in b){c+=a+":"+b[a]+"\n"}if(console){console.log(c)}}};bigshot.Browser=function(){this.requestAnimationFrameFunction=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||function(b,a){return setTimeout(b,0)}};bigshot.Browser.prototype={removeAllChildren:function(a){a.innerHTML=""},mouseEnter:function(b){var a=this.isAChildOf;return function(c){var d=c.relatedTarget;if(this===d||a(this,d)){return}b.call(this,c)}},isAChildOf:function(b,a){if(b===a){return false}while(a&&a!==b){a=a.parentNode}return a===b},unregisterListener:function(d,b,c,a){if(typeof(d.removeEventListener)!="undefined"){d.removeEventListener(b,c,a)}else{if(typeof(d.detachEvent)!="undefined"){d.detachEvent("on"+b,c)}}},registerListener:function(a,b,d,c){if(typeof a.addEventListener!="undefined"){if(b==="mouseenter"){a.addEventListener("mouseover",this.mouseEnter(d),c)}else{if(b==="mouseleave"){a.addEventListener("mouseout",this.mouseEnter(d),c)}else{a.addEventListener(b,d,c)}}}else{if(typeof a.attachEvent!="undefined"){a.attachEvent("on"+b,d)}else{a["on"+b]=d}}},stopEventBubbling:function(a){if(a){if(a.stopPropagation){a.stopPropagation()}else{a.cancelBubble=true}}},stopEventBubblingHandler:function(){var a=this;return function(b){a.stopEventBubbling(b);return false}},stopMouseEventBubbling:function(a){this.registerListener(a,"mousedown",this.stopEventBubblingHandler(),false);this.registerListener(a,"mouseup",this.stopEventBubblingHandler(),false);this.registerListener(a,"mousemove",this.stopEventBubblingHandler(),false)},getElementSize:function(b){var a={};if(b.clientWidth){a.w=b.clientWidth}if(b.clientHeight){a.h=b.clientHeight}return a},browserIsViewporting:function(){if(window.innerWidth<=screen.width){return false}else{return true}},getDevicePixelScale:function(){if(this.browserIsViewporting()){return screen.width/window.innerWidth}else{return 1}},requestAnimationFrame:function(c,a){var b=this.requestAnimationFrameFunction;b(c,a)},getElementPosition:function(b){var a=new Object();a.x=0;a.y=0;var c=b;while(c){a.x+=c.offsetLeft;a.y+=c.offsetTop;if(c.clientLeft){a.x+=c.clientLeft}if(c.clientTop){a.y+=c.clientTop}if(c.x){a.x+=c.x}if(c.y){a.y+=c.y}c=c.offsetParent}return a},createXMLHttpRequest:function(){try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(a){}try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(a){}try{return new XMLHttpRequest()}catch(a){}alert("XMLHttpRequest not supported");return null},makeOpacityTransition:function(a,b){if(a.style.WebkitTransitionProperty!=undefined){a.style.opacity=1;a.style.WebkitTransitionProperty="opacity";a.style.WebkitTransitionTimingFunction="linear";a.style.WebkitTransitionDuration="1s";setTimeout(function(){a.addEventListener("webkitTransitionEnd",function(){b()});a.style.opacity=0},0)}else{a.style.opacity=0;b()}}};bigshot.EventDispatcher=function(){this.eventListeners={}};bigshot.EventDispatcher.prototype={addEventListener:function(a,b){if(this.eventListeners[a]==undefined){this.eventListeners[a]=new Array()}this.eventListeners[a].push(b)},removeEventListener:function(a,d){if(this.eventListeners[a]!=undefined){var c=this.eventListeners[a];for(var b=0;b<c.length;++b){if(c[b]===listener){c.splice(b,1);if(c.length==0){delete this.eventListeners[a]}break}}}},fireEvent:function(b,a){if(this.eventListeners[b]!=undefined){var d=this.eventListeners[b];for(var c=0;c<d.length;++c){d[c](a)}}}};bigshot.Event=function(b){this.bubbles=false;this.cancelable=false;this.currentTarget=null;this.defaultPrevented=false;this.target=null;this.timeStamp=new Date().getTime();this.type=null;this.isTrusted=false;for(var a in b){this[a]=b[a]}};bigshot.Event.prototype={preventDefault:function(){this.defaultPrevented=true}};bigshot.TimedWeakReference=function(b,c,a){this.object=null;this.hasObject=false;this.fnCreate=b;this.fnDispose=c;this.lastAccess=new Date().getTime();this.hasTimer=false;this.interval=a};bigshot.TimedWeakReference.prototype={dispose:function(){this.clear()},get:function(){if(!this.hasObject){this.hasObject=true;this.object=this.fnCreate();this.startTimer()}this.lastAccess=new Date().getTime();return this.object},clear:function(){if(this.hasObject){this.hasObject=false;this.fnDispose(this.object);this.object=null;this.stopTimer()}},stopTimer:function(){if(this.hasTimer){clearTimeout(this.timerId);this.hasTimer=false}},startTimer:function(){if(!this.hasTimer){var a=this;this.hasTimer=true;this.timerId=setTimeout(function(){a.hasTimer=false;a.update()},this.interval)}},update:function(){if(this.hasObject){var a=new Date().getTime();if(a-this.lastAccess>this.interval){this.clear()}else{this.startTimer()}}}};bigshot.ImageEvent=function(a){bigshot.Event.call(this,a)};bigshot.ImageEvent.prototype={};bigshot.Object.extend(bigshot.ImageEvent,bigshot.Event);bigshot.VREvent=function(a){bigshot.Event.call(this,a)};bigshot.VREvent.prototype={};bigshot.Object.extend(bigshot.VREvent,bigshot.Event);bigshot.FullScreen=function(a){this.container=a;this.isFullScreen=false;this.savedBodyStyle=null;this.savedParent=null;this.savedSize=null;this.expanderDiv=null;this.restoreSize=false;this.onCloseHandlers=new Array();this.onResizeHandlers=new Array();var b=function(d,e){for(var c=0;c<e.length;++c){if(d[e[c]]){return e[c]}}return null};this.requestFullScreen=b(a,["requestFullScreen","mozRequestFullScreen","webkitRequestFullScreen"]);this.cancelFullScreen=b(document,["cancelFullScreen","mozCancelFullScreen","webkitCancelFullScreen"]);this.restoreSize=this.requestFullScreen!=null};bigshot.FullScreen.prototype={browser:new bigshot.Browser(),getRootElement:function(){return this.div},addOnClose:function(a){this.onCloseHandlers.push(a)},onClose:function(){for(var a=0;a<this.onCloseHandlers.length;++a){this.onCloseHandlers[a]()}},addOnResize:function(a){this.onResizeHandlers.push(a)},onResize:function(){for(var a=0;a<this.onResizeHandlers.length;++a){this.onResizeHandlers[a]()}},open:function(){this.isFullScreen=true;if(this.requestFullScreen){return this.openRequestFullScreen()}else{return this.openCompat()}},openRequestFullScreen:function(){this.savedSize={width:this.container.style.width,height:this.container.style.height};this.container.style.width="100%";this.container.style.height="100%";var a=this;if(this.requestFullScreen=="mozRequestFullScreen"){var c=function(){a.container.removeEventListener("mozfullscreenerror",c);a.isFullScreen=false;a.exitFullScreenHandler();a.onClose()};this.container.addEventListener("mozfullscreenerror",c);var b=function(){if(document.mozFullScreenElement!==a.container){document.removeEventListener("mozfullscreenchange",b);a.exitFullScreenHandler()}else{a.onResize()}};document.addEventListener("mozfullscreenchange",b)}else{var b=function(){if(document.webkitCurrentFullScreenElement!==a.container){a.container.removeEventListener("webkitfullscreenchange",b);a.exitFullScreenHandler()}else{a.onResize()}};this.container.addEventListener("webkitfullscreenchange",b)}this.exitFullScreenHandler=function(){if(a.isFullScreen){a.isFullScreen=false;document[a.cancelFullScreen]();if(a.restoreSize){a.container.style.width=a.savedSize.width;a.container.style.height=a.savedSize.height}a.onResize();a.onClose()}};this.container[this.requestFullScreen]()},openCompat:function(){this.savedParent=this.container.parentNode;this.savedSize={width:this.container.style.width,height:this.container.style.height};this.savedBodyStyle=document.body.style.cssText;document.body.style.overflow="hidden";this.expanderDiv=document.createElement("div");this.expanderDiv.style.position="absolute";this.expanderDiv.style.top="0px";this.expanderDiv.style.left="0px";this.expanderDiv.style.width=Math.max(window.innerWidth,document.documentElement.clientWidth)+"px";this.expanderDiv.style.height=Math.max(window.innerHeight,document.documentElement.clientHeight)+"px";document.body.appendChild(this.expanderDiv);this.div=document.createElement("div");this.div.style.position="fixed";this.div.style.top=window.pageYOffset+"px";this.div.style.left=window.pageXOffset+"px";this.div.style.width=window.innerWidth+"px";this.div.style.height=window.innerHeight+"px";this.div.style.zIndex=9998;this.div.appendChild(this.container);document.body.appendChild(this.div);var c=this;var b=function(f){setTimeout(function(){c.div.style.width=window.innerWidth+"px";c.div.style.height=window.innerHeight+"px";setTimeout(function(){c.onResize()},1)},1)};var d=function(f){c.expanderDiv.style.width=Math.max(window.innerWidth,document.documentElement.clientWidth)+"px";c.expanderDiv.style.height=Math.max(window.innerHeight,document.documentElement.clientHeight)+"px";setTimeout(function(){c.div.style.top=window.pageYOffset+"px";c.div.style.left=window.pageXOffset+"px";c.div.style.width=window.innerWidth+"px";c.div.style.height=window.innerHeight+"px";setTimeout(function(){c.onResize()},1)},1)};var a=function(f){if(f.keyCode==27){c.exitFullScreenHandler()}};this.exitFullScreenHandler=function(){c.isFullScreen=false;c.browser.unregisterListener(document,"keydown",a);c.browser.unregisterListener(window,"resize",b);c.browser.unregisterListener(document.body,"orientationchange",d);if(c.restoreSize){c.container.style.width=c.savedSize.width;c.container.style.height=c.savedSize.height}document.body.style.cssText=c.savedBodyStyle;c.savedParent.appendChild(c.container);document.body.removeChild(c.div);document.body.removeChild(c.expanderDiv);c.onResize();c.onClose();setTimeout(function(){c.onResize()},1)};this.browser.registerListener(document,"keydown",a,false);this.browser.registerListener(window,"resize",b,false);this.browser.registerListener(document.body,"orientationchange",d,false);this.onResize();return this.exitFullScreenHandler},close:function(){this.exitFullScreenHandler()}};bigshot.DataLoader=function(){};bigshot.DataLoader.prototype={loadImage:function(a,b){},loadXml:function(a,b,c){}};bigshot.DefaultDataLoader=function(b,a){this.maxRetries=b;this.crossOrigin=a;if(!this.maxRetries){this.maxRetries=0}};bigshot.DefaultDataLoader.prototype={browser:new bigshot.Browser(),loadImage:function(a,d){var c=document.createElement("img");c.retries=0;if(this.crossOrigin!=null){c.crossOrigin=this.crossOrigin}var b=this;this.browser.registerListener(c,"load",function(){if(d){d(c)}},false);this.browser.registerListener(c,"error",function(){c.retries++;if(c.retries<=b.maxRetries){setTimeout(function(){c.src=a},c.retries*1000)}else{if(d){d(null)}}},false);c.src=a;return c},loadXml:function(c,a,f){for(var e=0;e<=this.maxRetries;++e){var d=this.browser.createXMLHttpRequest();d.open("GET",c,false);d.send(null);if(d.status==200){var b=d.responseXML;if(b!=null){if(f){f(b)}return b}}if(e==that.maxRetries){if(f){f(null)}return null}}}};bigshot.Object.validate("bigshot.DefaultDataLoader",bigshot.DataLoader);bigshot.CachingDataLoader=function(){this.cache={};this.requested={};this.requestedTiles={}};bigshot.CachingDataLoader.prototype={browser:new bigshot.Browser(),loadImage:function(a,d){if(this.cache[a]){if(d){d(this.cache[a])}return this.cache[a]}else{if(this.requested[a]){if(d){this.requested[a].push(d)}return this.requestedTiles[a]}else{var c=this;this.requested[a]=new Array();if(d){this.requested[a].push(d)}var b=document.createElement("img");this.requestedTiles[a]=b;this.browser.registerListener(b,"load",function(){var f=c.requested[a];delete c.requested[a];delete c.requestedTiles[a];c.cache[a]=b;for(var e=0;e<f.length;++e){f[e](b)}},false);b.src=a;return b}}},loadXml:function(a,c,f){if(this.cache[a]){if(f){f(this.cache[a])}return this.cache[a]}else{if(this.requested[a]&&c){if(f){this.requested[a].push(f)}}else{var e=this.browser.createXMLHttpRequest();if(!this.requested[a]){this.requested[a]=new Array()}if(c){if(f){this.requested[a].push(f)}}var d=this;var b=function(){if(d.requested[a]){var g=null;if(e.status==200){g=e.responseXML}var k=d.requested[a];delete d.requested[a];d.cache[a]=g;for(var j=0;j<k.length;++j){k[j](g)}}return g};if(c){e.onreadystatechange=function(){if(e.readyState==4){b()}};e.open("GET",a,true);e.send()}else{e.open("GET",a,false);e.send();return b()}}}}};bigshot.Object.validate("bigshot.CachingDataLoader",bigshot.DataLoader);bigshot.Hotspot=function(a,e,b,d){var c=document.createElement("div");c.style.position="absolute";c.style.overflow="visible";this.element=c;this.x=a;this.y=e;this.w=b;this.h=d};bigshot.Hotspot.prototype={browser:new bigshot.Browser(),layout:function(c,e,d){var g=this.x*d+c;var f=this.y*d+e;var a=this.w*d;var b=this.h*d;this.element.style.top=f+"px";this.element.style.left=g+"px";this.element.style.width=a+"px";this.element.style.height=b+"px"},getElement:function(){return this.element}};bigshot.PointHotspot=function(a,i,b,e,f,g,c){bigshot.Hotspot.call(this,a,i,b,e);this.xo=f;this.yo=g;if(c){var d=this.getElement();d.style.backgroundImage="url('"+c+"')";d.style.backgroundRepeat="no-repeat"}};bigshot.PointHotspot.prototype={getLabel:function(){return this.label},layout:function(a,c,b){var e=this.x*b+a+this.xo;var d=this.y*b+c+this.yo;this.element.style.top=d+"px";this.element.style.left=e+"px";this.element.style.width=this.w+"px";this.element.style.height=this.h+"px"}};bigshot.Object.extend(bigshot.PointHotspot,bigshot.Hotspot);bigshot.Layer=function(){};bigshot.Layer.prototype={getContainer:function(){},setMaxTiles:function(a,b){},resize:function(a,b){},layout:function(g,e,f,a,d,c,i,b){}};bigshot.LabeledHotspot=function(a,e,b,c,d){bigshot.Hotspot.call(this,a,e,b,c);this.label=document.createElement("div");this.label.style.position="relative";this.label.style.display="inline-block";this.getElement().appendChild(this.label);this.label.innerHTML=d;this.labelSize=this.browser.getElementSize(this.label)};bigshot.LabeledHotspot.prototype={getLabel:function(){return this.label},layout:function(c,e,d){this.layout._super.call(this,c,e,d);var a=this.w*d;var b=this.h*d;this.label.style.top=(b+4)+"px";this.label.style.left=((a-this.labelSize.w)/2)+"px"}};bigshot.Object.extend(bigshot.LabeledHotspot,bigshot.Hotspot);bigshot.LinkHotspot=function(a,f,b,d,e,c){bigshot.LabeledHotspot.call(this,a,f,b,d,e);this.browser.registerListener(this.getElement(),"click",function(){document.location.href=c})};bigshot.Object.extend(bigshot.LinkHotspot,bigshot.LabeledHotspot);bigshot.HotspotLayer=function(a){this.image=a;this.hotspots=new Array();this.browser=new bigshot.Browser();this.container=a.createLayerContainer();this.parentContainer=a.getContainer();this.resize(0,0)};bigshot.HotspotLayer.prototype={getContainer:function(){return this.container},resize:function(a,b){this.container.style.width=this.parentContainer.clientWidth+"px";this.container.style.height=this.parentContainer.clientHeight+"px"},layout:function(k,d,j,e,a,l,b,g){var c=Math.pow(2,this.image.getZoom());d-=b*e;j-=b*a;for(var f=0;f<this.hotspots.length;++f){this.hotspots[f].layout(d,j,c)}},setMaxTiles:function(b,a){},addHotspot:function(a){this.container.appendChild(a.getElement());this.hotspots.push(a)}};bigshot.Object.validate("bigshot.HotspotLayer",bigshot.Layer);bigshot.TileLayer=function(d,c,a,b,e){this.rows=new Array();this.browser=new bigshot.Browser();this.container=d.createLayerContainer();this.parentContainer=d.getContainer();this.parameters=c;this.w=a;this.h=b;this.imageTileCache=e;this.resize(a,b);return this};bigshot.TileLayer.prototype={getContainer:function(){return this.container},resize:function(a,d){this.container.style.width=this.parentContainer.clientWidth+"px";this.container.style.height=this.parentContainer.clientHeight+"px";this.pixelWidth=this.parentContainer.clientWidth;this.pixelHeight=this.parentContainer.clientHeight;this.w=a;this.h=d;this.rows=new Array();this.browser.removeAllChildren(this.container);for(var f=0;f<d;++f){var g=new Array();for(var i=0;i<a;++i){var b=document.createElement("div");b.style.position="absolute";b.style.overflow="hidden";b.style.width=this.container.clientWidth+"px";b.style.height=this.container.clientHeight+"px";var e=document.createElement("div");e.style.position="relative";e.style.border="hidden";e.style.visibility="hidden";e.bigshotData={visible:false};g.push(e);this.container.appendChild(b);b.appendChild(e)}this.rows.push(g)}},layout:function(a,p,d,z,f,m,o,e){a=Math.min(0,Math.ceil(a));this.imageTileCache.resetUsed();var g=d;var b=0;for(var k=0;k<this.h;++k){var i=p;for(var s=0;s<this.w;++s){var v=this.rows[k][s];var j=v.bigshotData;if(i+m<0||i>this.pixelWidth||g+m<0||g>this.pixelHeight){if(j.visible){j.visible=false;v.style.visibility="hidden"}}else{b++;v.style.left=i+"px";v.style.top=g+"px";v.style.width=m+"px";v.style.height=m+"px";v.style.opacity=e;if(!j.visible){j.visible=true;v.style.visibility="visible"}var u=s+z;var t=k+f;if(this.parameters.wrapX){if(u<0||u>=this.imageTileCache.maxTileX){u=(u+this.imageTileCache.maxTileX)%this.imageTileCache.maxTileX}}if(this.parameters.wrapY){if(t<0||t>=this.imageTileCache.maxTileY){t=(t+this.imageTileCache.maxTileY)%this.imageTileCache.maxTileY}}var n=u+"_"+t+"_"+a;var q=u<0||u>=this.imageTileCache.maxTileX||t<0||t>=this.imageTileCache.maxTileY;if(q){if(!j.isOutside){var l=this.imageTileCache.getImage(u,t,a);this.browser.removeAllChildren(v);v.appendChild(l);j.image=l}j.isOutside=true;j.imageKey="EMPTY";j.image.style.width=m+"px";j.image.style.height=m+"px"}else{var l=this.imageTileCache.getImage(u,t,a);j.isOutside=false;if(j.imageKey!==n||j.isPartial){this.browser.removeAllChildren(v);v.appendChild(l);j.image=l;j.imageKey=n;j.isPartial=l.isPartial}j.image.style.width=m+"px";j.image.style.height=m+"px"}}i+=o}g+=o}},setMaxTiles:function(b,a){this.imageTileCache.setMaxTiles(b,a)}};bigshot.Object.validate("bigshot.TileLayer",bigshot.Layer);bigshot.LRUMap=function(){this.keyToTime={};this.counter=0;this.size=0};bigshot.LRUMap.prototype={access:function(a){this.remove(a);this.keyToTime[a]=this.counter;++this.counter;++this.size},remove:function(a){if(this.keyToTime[a]){delete this.keyToTime[a];--this.size;return true}else{return false}},getSize:function(){return this.size},leastUsed:function(){var b=this.counter+1;var c=null;for(var a in this.keyToTime){if(this.keyToTime[a]<b){b=this.keyToTime[a];c=a}}return c}};bigshot.ImageTileCache=function(d,a,c){var b=this;this.parameters=c;this.fullImage=null;c.dataLoader.loadImage(c.fileSystem.getPosterFilename(),function(e){b.fullImage=e;if(a){a()}});this.maxCacheSize=512;this.maxTileX=0;this.maxTileY=0;this.cachedImages={};this.requestedImages={};this.usedImages={};this.lastOnLoadFiredAt=0;this.imageRequests=0;this.lruMap=new bigshot.LRUMap();this.onLoaded=d;this.browser=new bigshot.Browser();this.partialImageSize=c.tileSize/4;this.POSTER_ZOOM_LEVEL=Math.log(c.posterSize/Math.max(c.width,c.height))/Math.log(2)};bigshot.ImageTileCache.prototype={resetUsed:function(){this.usedImages={}},setMaxTiles:function(b,a){this.maxTileX=b;this.maxTileY=a},getPartialImage:function(c,b,d){var a=this.getPartialImageFromDownsampled(c,b,d,0,0,this.parameters.tileSize,this.parameters.tileSize);if(a==null){a=this.getPartialImageFromPoster(c,b,d)}return a},getPartialImageFromPoster:function(d,c,e){if(this.fullImage&&this.fullImage.complete){var a=this.fullImage.width/this.parameters.width;var b=a*this.parameters.tileSize/Math.pow(2,e);x0=Math.floor(b*d);y0=Math.floor(b*c);w=Math.floor(b);h=Math.floor(b);return this.createPartialImage(this.fullImage,this.fullImage.width,x0,y0,w,h)}else{return null}},createPartialImage:function(f,k,b,p,q,g){var c=document.createElement("canvas");if(!c.width){return null}c.width=this.partialImageSize;c.height=this.partialImageSize;var r=c.getContext("2d");var d=f.width/k;var o=Math.floor(b*d);var n=Math.floor(p*d);var a=this.partialImageSize;var l=this.partialImageSize;q*=d;if(o+q>=f.width){var i=q;q=f.width-o;a*=q/i}g*=d;if(n+g>=f.height){var m=g;g=f.height-n;l*=g/m}try{r.drawImage(f,o,n,q,g,-0.1,-0.1,a+0.2,l+0.2)}catch(j){return null}return c},getPartialImageFromDownsampled:function(a,j,d,b,f,g,e){if(d<this.POSTER_ZOOM_LEVEL||d<this.parameters.minZoom){return null}var i=this.getImageKey(a,j,d);var c=this.cachedImages[i];if(c==null){this.requestImage(a,j,d)}if(c){return this.createPartialImage(c,this.parameters.tileSize,b,f,g,e)}else{g/=2;e/=2;b/=2;f/=2;if((a%2)==1){b+=this.parameters.tileSize/2}if((j%2)==1){f+=this.parameters.tileSize/2}a=Math.floor(a/2);j=Math.floor(j/2);--d;return this.getPartialImageFromDownsampled(a,j,d,b,f,g,e)}},getEmptyImage:function(){var a=document.createElement("img");if(this.parameters.emptyImage){a.src=this.parameters.emptyImage}else{a.src="data:image/gif,GIF89a%01%00%01%00%80%00%00%00%00%00%FF%FF%FF!%F9%04%00%00%00%00%00%2C%00%00%00%00%01%00%01%00%00%02%02D%01%00%3B"}return a},getImage:function(e,d,f){if(e<0||d<0||e>=this.maxTileX||d>=this.maxTileY){return this.getEmptyImage()}var b=this.getImageKey(e,d,f);this.lruMap.access(b);if(this.cachedImages[b]){if(this.usedImages[b]){var c=this.parameters.dataLoader.loadImage(this.getImageFilename(e,d,f));c.isPartial=false;return c}else{this.usedImages[b]=true;var a=this.cachedImages[b];return a}}else{this.requestImage(e,d,f);var a=this.getPartialImage(e,d,f);if(a!=null){a.isPartial=true;this.cachedImages[b]=a}else{a=this.getEmptyImage();if(a!=null){a.isPartial=true}}return a}},requestImage:function(d,c,e){var a=this.getImageKey(d,c,e);if(!this.requestedImages[a]){this.imageRequests++;var b=this;this.requestedImages[a]=true;this.parameters.dataLoader.loadImage(this.getImageFilename(d,c,e),function(f){delete b.requestedImages[a];b.imageRequests--;f.isPartial=false;b.cachedImages[a]=f;b.fireOnLoad()})}},fireOnLoad:function(){var a=new Date();if(this.imageRequests==0||a.getTime()>(this.lastOnLoadFiredAt+50)){this.purgeCache();this.lastOnLoadFiredAt=a.getTime();this.onLoaded()}},purgeCache:function(){for(var a=0;a<4;++a){if(this.lruMap.getSize()>this.maxCacheSize){var b=this.lruMap.leastUsed();this.lruMap.remove(b);delete this.cachedImages[b]}}},getImageKey:function(b,a,c){return"I"+b+"_"+a+"_"+c},getImageFilename:function(c,a,d){var b=this.parameters.fileSystem.getImageFilename(c,a,d);return b}};bigshot.ImageParameters=function(b){this.posterSize=0;this.emptyImage=null;this.suffix=null;this.width=0;this.height=0;this.container=null;this.minZoom=0;this.maxZoom=0;this.tileSize=0;this.overlap=0;this.wrapX=false;this.wrapY=false;this.basePath=null;this.fileSystemType="folder";this.fileSystem=null;this.dataLoader=new bigshot.DefaultDataLoader();this.touchUI=false;this.fling=true;this.maxTextureMagnification=1;if(b){for(var a in b){this[a]=b[a]}}this.merge=function(d,e){for(var c in d){if(e||!this[c]){this[c]=d[c]}}};return this};bigshot.ImageBase=function(b){bigshot.EventDispatcher.call(this);this.parameters=b;this.flying=0;this.container=b.container;this.x=b.width/2;this.y=b.height/2;this.zoom=0;this.width=b.width;this.height=b.height;this.minZoom=b.minZoom;this.maxZoom=b.maxZoom;this.tileSize=b.tileSize;this.overlap=0;this.imageTileCache=null;this.dragStart=null;this.dragged=false;this.layers=new Array();this.fullScreenHandler=null;this.currentGesture=null;var a=this;this.onresizeHandler=function(f){a.onresize()};var d=function(e){if(e.preventDefault){e.preventDefault()}return false};var c=function(e){if(e.clientX){return e}else{return{clientX:e.changedTouches[0].clientX,clientY:e.changedTouches[0].clientY,changedTouches:e.changedTouches}}};this.setupLayers();this.resize();this.allListeners={DOMMouseScroll:function(f){a.mouseWheel(f);return d(f)},mousewheel:function(f){a.mouseWheel(f);return d(f)},dblclick:function(f){a.mouseDoubleClick(f);return d(f)},mousedown:function(f){a.dragMouseDown(f);return d(f)},gesturestart:function(f){a.gestureStart(f);return d(f)},gesturechange:function(f){a.gestureChange(f);return d(f)},gestureend:function(f){a.gestureEnd(f);return d(f)},touchstart:function(f){a.dragMouseDown(c(f));return d(f)},mouseup:function(f){a.dragMouseUp(f);return d(f)},touchend:function(f){a.dragMouseUp(c(f));return d(f)},mousemove:function(f){a.dragMouseMove(f);return d(f)},mouseout:function(f){return d(f)},touchmove:function(f){a.dragMouseMove(c(f));return d(f)}};this.addEventListeners();this.browser.registerListener(window,"resize",a.onresizeHandler,false);this.zoomToFit()};bigshot.ImageBase.prototype={browser:new bigshot.Browser(),addEventListeners:function(){for(var a in this.allListeners){this.browser.registerListener(this.container,a,this.allListeners[a],false)}},removeEventListeners:function(){for(var a in this.allListeners){this.browser.unregisterListener(this.container,a,this.allListeners[a],false)}},setupLayers:function(){},getTextureStretch:function(){var a=Math.log(this.parameters.maxTextureMagnification/this.browser.getDevicePixelScale())/Math.LN2;return a},clampXY:function(j,g){var e=this.container.clientWidth;var d=this.container.clientHeight;var f=Math.pow(2,this.zoom);var b=e/f;var a=d/f;var i=function(m,o,n){var l=m/2;l=Math.min(o/2,l);if(n<l){n=l}var k=o-m/2;k=Math.max(o/2,k);if(n>k){n=k}return n};var c={};if(j!=null){c.x=i(b,this.width,j)}if(g!=null){c.y=i(a,this.height,g)}return c},layout:function(){var o=this.container.clientWidth;var c=this.container.clientHeight;var p=Math.min(this.maxZoom,Math.max(this.zoom-this.getTextureStretch(),this.minZoom));var l=Math.min(0,Math.ceil(p));var e=Math.pow(2,l);var m=this.clampXY(this.x,this.y);if(!this.parameters.wrapY){this.y=m.y}if(!this.parameters.wrapX){this.x=m.x}var u=this.tileSize/e;var j=Math.pow(2,this.zoom-l);var d=this.tileSize*j;var n=this.width/u;var k=this.height/u;var g=this.x/u;var f=this.y/u;var b=g-(o/2)/d;var a=f-(c/2)/d;var s=Math.floor(b);var r=Math.floor(a);var v=Math.round((b-s)*d);var t=Math.round((a-r)*d);for(var q=0;q<this.layers.length;++q){this.layers[q].layout(p,-v-d,-t-d,s-1,r-1,Math.ceil(d),Math.ceil(d),1)}},resize:function(){var c=Math.ceil(2*this.container.clientWidth/this.tileSize)+2;var a=Math.ceil(2*this.container.clientHeight/this.tileSize)+2;for(var b=0;b<this.layers.length;++b){this.layers[b].resize(c,a)}},createLayerContainer:function(){var a=document.createElement("div");a.style.position="absolute";a.style.overflow="hidden";return a},getContainer:function(){return this.container},addLayer:function(a){this.container.appendChild(a.getContainer());this.layers.push(a)},clampZoom:function(a){return Math.min(this.maxZoom,Math.max(a,this.minZoom))},setZoom:function(c,e){this.zoom=this.clampZoom(c);var g=Math.ceil(this.zoom-this.getTextureStretch());var b=Math.pow(2,g);var f=Math.ceil(b*this.width/this.tileSize);var d=Math.ceil(b*this.height/this.tileSize);for(var a=0;a<this.layers.length;++a){this.layers[a].setMaxTiles(f,d)}if(e){this.layout()}},setMaxZoom:function(a){this.maxZoom=a},getMaxZoom:function(){return this.maxZoom},setMinZoom:function(a){this.minZoom=a},getMinZoom:function(){return this.minZoom},adjustCoordinateForZoom:function(e,a,d,c){var b=Math.pow(2,d)/Math.pow(2,c);return a+(e-a)*b},gestureStart:function(a){this.currentGesture={startZoom:this.zoom,scale:a.scale}},gestureEnd:function(a){this.currentGesture=null;if(this.dragStart){this.dragStart.hadGesture=true}},gestureChange:function(d){if(this.currentGesture){if(this.dragStart){this.dragStart.hadGesture=true}var c=this.clampZoom(this.currentGesture.startZoom+Math.log(d.scale)/Math.log(2));var e=this.getZoom();if(this.currentGesture.clientX!==undefined&&this.currentGesture.clientY!==undefined){var b=this.clientToImage(this.currentGesture.clientX,this.currentGesture.clientY);var a=this.adjustCoordinateForZoom(this.x,b.x,e,c);var f=this.adjustCoordinateForZoom(this.y,b.y,e,c);this.moveTo(a,f,c)}else{this.setZoom(c);this.layout()}}},dragMouseDown:function(a){this.dragStart={x:a.clientX,y:a.clientY};this.dragLast={clientX:a.clientX,clientY:a.clientY,dx:0,dy:0,dt:1000000,time:new Date().getTime()};this.dragged=false},dragMouseMove:function(a){if(this.currentGesture!=null&&a.changedTouches!=null&&a.changedTouches.length>0){var e=0;var d=0;for(var j=0;j<a.changedTouches.length;++j){e+=a.changedTouches[j].clientX;d+=a.changedTouches[j].clientY}this.currentGesture.clientX=e/a.changedTouches.length;this.currentGesture.clientY=d/a.changedTouches.length}if(this.currentGesture==null&&this.dragStart!=null){var k={x:a.clientX-this.dragStart.x,y:a.clientY-this.dragStart.y};if(k.x!=0||k.y!=0){this.dragged=true}var c=Math.pow(2,this.zoom);var g=k.x/c;var f=k.y/c;this.dragStart={x:a.clientX,y:a.clientY};var b=new Date().getTime()-this.dragLast.time;if(b>20){this.dragLast={dx:this.dragLast.clientX-a.clientX,dy:this.dragLast.clientY-a.clientY,dt:b,clientX:a.clientX,clientY:a.clientY,time:new Date().getTime()}}this.moveTo(this.x-g,this.y-f)}},dragMouseUp:function(a){if(this.currentGesture==null&&!this.dragStart.hadGesture&&this.dragStart!=null){this.dragStart=null;if(!this.dragged){this.mouseClick(a)}else{var e=Math.pow(2,this.zoom);var j=this.dragLast.dx/e;var i=this.dragLast.dy/e;var d=Math.sqrt(j*j+i*i);var c=this.dragLast.dt;var b=new Date().getTime()-this.dragLast.time;this.dragLast=null;var g=c>0?(d/c):0;if(g>0.05&&b<250&&c>20&&this.parameters.fling){var f=new Date().getTime();j/=c;i/=c;this.flyTo(this.x+j*250,this.y+i*250,this.zoom)}}}},mouseDoubleClick:function(b){var a=this.createImageEventData({type:"dblclick",clientX:b.clientX,clientY:b.clientY});this.fireEvent("dblclick",a);if(!a.defaultPrevented){this.flyTo(a.imageX,a.imageY,this.zoom+0.5)}},getZoom:function(){return this.zoom},moveTo:function(a,d,b,c){this.stopFlying();if(a!=null||d!=null){this.setPosition(a,d,false)}if(b!=null){this.setZoom(b,false)}if(c==undefined||c==true){this.layout()}},setPosition:function(a,d,c){var b=this.clampXY(a,d);if(a!=null){if(this.parameters.wrapX){if(a<0||a>=this.width){a=(a+this.width)%this.width}}else{a=b.x}this.x=Math.max(0,Math.min(this.width,a))}if(d!=null){if(this.parameters.wrapY){if(d<0||d>=this.height){d=(d+this.height)%this.height}}else{d=b.y}this.y=Math.max(0,Math.min(this.height,d))}if(c!=false){this.layout()}},fitZoom:function(c,a){var b=a/c;return Math.log(b)/Math.LN2},getZoomToFitValue:function(){return Math.min(this.fitZoom(this.parameters.width,this.container.clientWidth),this.fitZoom(this.parameters.height,this.container.clientHeight))},getZoomToFillValue:function(){return Math.max(this.fitZoom(this.parameters.width,this.container.clientWidth),this.fitZoom(this.parameters.height,this.container.clientHeight))},zoomToFit:function(){this.moveTo(null,null,this.getZoomToFitValue())},zoomToFill:function(){this.moveTo(null,null,this.getZoomToFillValue())},zoomToFitHeight:function(){this.moveTo(null,null,this.fitZoom(this.parameters.height,this.container.clientHeight))},zoomToFitWidth:function(){this.moveTo(null,null,this.fitZoom(this.parameters.width,this.container.clientWidth))},flyZoomToFitHeight:function(){this.flyTo(null,this.parameters.height/2,this.fitZoom(this.parameters.height,this.container.clientHeight))},flyZoomToFitWidth:function(){this.flyTo(this.parameters.width/2,null,this.fitZoom(this.parameters.width,this.container.clientWidth))},flyZoomToFit:function(){this.flyTo(this.parameters.width/2,this.parameters.height/2,this.getZoomToFitValue())},clientToImage:function(c,a){var b=Math.pow(2,this.zoom);return{x:(c-this.container.clientWidth/2)/b+this.x,y:(a-this.container.clientHeight/2)/b+this.y}},mouseWheelHandler:function(g,d){var e=false;if(g>0){e=0.5}else{if(g<0){e=-0.5}}if(e){var b=this.clientToImage(d.clientX,d.clientY);var c=Math.min(this.maxZoom,Math.max(this.getZoom()+e,this.minZoom));var a=this.adjustCoordinateForZoom(this.x,b.x,this.getZoom(),c);var f=this.adjustCoordinateForZoom(this.y,b.y,this.getZoom(),c);this.flyTo(a,f,c,true)}},mouseWheel:function(a){var b=0;if(!a){a=window.event}if(a.wheelDelta){b=a.wheelDelta/120;if(window.opera){b=-b}}else{if(a.detail){b=-a.detail}}if(b){this.mouseWheelHandler(b,a)}if(a.preventDefault){a.preventDefault()}a.returnValue=false},onresize:function(){this.resize();this.layout()},getX:function(){return this.x},getY:function(){return this.y},stopFlying:function(){this.flying++},flyTo:function(m,l,q,e){var g=this;m=m!=null?m:this.x;l=l!=null?l:this.y;q=q!=null?q:this.zoom;e=e!=null?e:false;var d=this.x;var c=this.y;var o=this.zoom;var n=this.clampXY(m,l);var k=this.parameters.wrapX?m:n.x;var i=this.parameters.wrapY?l:n.y;var b=Math.min(this.maxZoom,Math.max(q,this.minZoom));this.flying++;var a=this.flying;var f=new Date().getTime();var p=function(z,x,t,s,r){var y=(x-z);var v=-y*Math.pow(2,-t*s);var u=t*r;if(y<0){v=Math.max(0,v-u)}else{v=Math.min(0,v+u)}return x+v};var j=function(){if(g.flying==a){var u=(new Date().getTime()-f)/1000;var r=p(d,k,u,e?10:4,e?0.2:1);var x=p(c,i,u,e?10:4,e?0.2:1);var v=p(o,b,u,10,0.2);var s=true;var t=Math.min(Math.pow(2,g.getZoom()),1);if(Math.abs(r-k)<(0.5*t)){r=k}else{s=false}if(Math.abs(x-i)<(0.5*t)){x=i}else{s=false}if(Math.abs(v-b)<0.02){v=b}else{s=false}g.setPosition(r,x,false);g.setZoom(v,false);g.layout();if(!s){g.browser.requestAnimationFrame(j,g.container)}}};this.browser.requestAnimationFrame(j,this.container)},rectVisibleAtZoomLevel:function(a,b){return Math.min(this.fitZoom(a,this.container.clientWidth),this.fitZoom(b,this.container.clientHeight))},getTouchAreaBaseSize:function(){var a=((this.container.clientWidth+this.container.clientHeight)/2)*0.2;return Math.min(a,Math.min(this.container.clientWidth,this.container.clientHeight)/6)},createImageEventData:function(b){var a=this.browser.getElementPosition(this.container);b.localX=b.clientX-a.x;b.localY=b.clientY-a.y;var c=Math.pow(2,this.zoom);b.imageX=(b.localX-this.container.clientWidth/2)/c+this.x;b.imageY=(b.localY-this.container.clientHeight/2)/c+this.y;b.target=this;b.currentTarget=this;return new bigshot.ImageEvent(b)},mouseClick:function(b){var a=this.createImageEventData({type:"click",clientX:b.clientX,clientY:b.clientY});this.fireEvent("click",a)},showTouchUI:function(f,g){if(!f){f=2500}if(!g){g=1000}var a=this.getTouchAreaBaseSize();var m=this.getTouchAreaBaseSize();var e=this.container.clientWidth/2;var d=this.container.clientHeight/2;var n=document.createElement("div");n.style.position="absolute";n.style.zIndex="9999";n.style.opacity=0.9;n.style.width=this.container.clientWidth+"px";n.style.height=this.container.clientHeight+"px";var b=document.createElement("div");b.style.position="absolute";var l=document.createElement("div");l.style.position="relative";l.style.background="black";l.style.textAlign="center";l.style.top=(d-m)+"px";l.style.left=(e-m)+"px";l.style.width=(2*m)+"px";l.style.height=(2*m)+"px";n.appendChild(b);b.appendChild(l);l.innerHTML="<span style='display:inline-box; position:relative; vertical-align:middle; font-size: 20pt; top: 10pt; color:white'>ZOOM IN</span>";var c=document.createElement("div");c.style.position="absolute";var k=document.createElement("div");k.style.position="relative";k.style.border=a+"px solid black";k.style.top="0px";k.style.left="0px";k.style.textAlign="center";k.style.width=this.container.clientWidth+"px";k.style.height=this.container.clientHeight+"px";k.style.MozBoxSizing=k.style.boxSizing=k.style.WebkitBoxSizing="border-box";k.innerHTML="<span style='position:relative; font-size: 20pt; top: -25pt; color:white'>ZOOM OUT</span>";c.appendChild(k);n.appendChild(c);this.container.appendChild(n);var j=this;var i=0.9;var p=g/50;if(p<1){p=1}var o=function(){i=i-(0.9/p);if(i<0){j.container.removeChild(n)}else{n.style.opacity=i;setTimeout(o,50)}};setTimeout(o,f)},exitFullScreen:function(){if(this.fullScreenHandler){this.removeEventListeners();this.fullScreenHandler.close();this.addEventListeners();this.fullScreenHandler=null;return}},fullScreen:function(a){if(this.fullScreenHandler){return}var c=document.createElement("div");c.style.position="absolute";c.style.fontSize="16pt";c.style.top="128px";c.style.width="100%";c.style.color="white";c.style.padding="16px";c.style.zIndex="9999";c.style.textAlign="center";c.style.opacity="0.75";c.innerHTML="<span style='border-radius: 16px; -moz-border-radius: 16px; padding: 16px; padding-left: 32px; padding-right: 32px; background:black'>Press Esc to exit full screen mode.</span>";var b=this;this.fullScreenHandler=new bigshot.FullScreen(this.container);this.fullScreenHandler.restoreSize=true;this.fullScreenHandler.addOnResize(function(){if(b.fullScreenHandler&&b.fullScreenHandler.isFullScreen){b.container.style.width=window.innerWidth+"px";b.container.style.height=window.innerHeight+"px"}b.onresize()});this.fullScreenHandler.addOnClose(function(){if(c.parentNode){try{div.removeChild(c)}catch(d){}}b.fullScreenHandler=null});if(a){this.fullScreenHandler.addOnClose(function(){a()})}this.removeEventListeners();this.fullScreenHandler.open();this.addEventListeners();if(this.fullScreenHandler.getRootElement()){this.fullScreenHandler.getRootElement().appendChild(c);setTimeout(function(){var e=0.75;var d=function(){e-=0.02;if(c.parentNode){if(e<=0){try{div.removeChild(c)}catch(f){}}else{c.style.opacity=e;setTimeout(d,20)}}};setTimeout(d,20)},3500)}return function(){b.fullScreenHandler.close()}},dispose:function(){this.browser.unregisterListener(window,"resize",this.onresizeHandler,false);this.removeEventListeners()}};bigshot.Object.extend(bigshot.ImageBase,bigshot.EventDispatcher);bigshot.Image=function(a){bigshot.setupFileSystem(a);a.merge(a.fileSystem.getDescriptor(),false);bigshot.ImageBase.call(this,a)};bigshot.Image.prototype={setupLayers:function(){var a=this;this.thisTileCache=new bigshot.ImageTileCache(function(){a.layout()},null,this.parameters);this.addLayer(new bigshot.TileLayer(this,this.parameters,0,0,this.thisTileCache))}};bigshot.Object.extend(bigshot.Image,bigshot.ImageBase);bigshot.HTMLElementLayer=function(d,b,c,a){this.hotspots=new Array();this.browser=new bigshot.Browser();this.image=d;this.container=d.createLayerContainer();this.parentContainer=d.getContainer();this.element=b;this.parentContainer.appendChild(b);this.w=c;this.h=a;this.resize(0,0)};bigshot.HTMLElementLayer.prototype={getContainer:function(){return this.container},resize:function(a,b){this.container.style.width=this.parentContainer.clientWidth+"px";this.container.style.height=this.parentContainer.clientHeight+"px"},layout:function(i,d,g,e,a,j,b,f){var c=Math.pow(2,this.image.getZoom());d-=b*e;g-=b*a;this.element.style.top=g+"px";this.element.style.left=d+"px";this.element.style.width=(this.w*c)+"px";this.element.style.height=(this.h*c)+"px"},setMaxTiles:function(b,a){}};bigshot.Object.validate("bigshot.HTMLElementLayer",bigshot.Layer);bigshot.HTMLDivElementLayer=function(e,b,c,a,f,d){this.wrapX=f;this.wrapY=d;this.hotspots=new Array();this.browser=new bigshot.Browser();this.image=e;this.container=e.createLayerContainer();this.parentContainer=e.getContainer();this.element=b;this.parentContainer.appendChild(b);this.w=c;this.h=a;this.resize(0,0)};bigshot.HTMLDivElementLayer.prototype={getContainer:function(){return this.container},resize:function(a,b){this.container.style.width=this.parentContainer.clientWidth+"px";this.container.style.height=this.parentContainer.clientHeight+"px"},layout:function(m,e,l,f,a,n,b,j){var d=Math.pow(2,this.image.getZoom());e-=b*f;l-=b*a;var c=(this.w*d);var k=(this.h*d);this.element.style.backgroundSize=c+"px "+k+"px";var i="0px";var g="0px";if(this.wrapY){this.element.style.top="0px";this.element.style.height=(this.parentContainer.clientHeight)+"px";g=l+"px"}else{this.element.style.top=l+"px";this.element.style.height=k+"px"}if(this.wrapX){this.element.style.left="0px";this.element.style.width=(this.parentContainer.clientWidth)+"px";i=e+"px"}else{this.element.style.left=e+"px";this.element.style.width=c+"px"}this.element.style.backgroundPosition=i+" "+g},setMaxTiles:function(b,a){}};bigshot.Object.validate("bigshot.HTMLDivElementLayer",bigshot.Layer);bigshot.SimpleImage=function(a,b){a.merge({fileSystem:null,fileSystemType:"simple",maxTextureMagnification:1,tileSize:1024},true);if(b){a.merge({width:b.width,height:b.height});this.imgElement=b}else{if(a.width==0||a.height==0){throw new Error("No imgElement and missing width or height in ImageParameters")}}bigshot.setupFileSystem(a);bigshot.ImageBase.call(this,a)};bigshot.SimpleImage.prototype={setupLayers:function(){if(!this.imgElement){this.imgElement=document.createElement("div");this.imgElement.style.backgroundImage="url('"+this.parameters.basePath+"')";this.imgElement.style.position="absolute";if(!this.parameters.wrapX&&!this.parameters.wrapY){this.imgElement.style.backgroundRepeat="no-repeat"}else{if(this.parameters.wrapX&&!this.parameters.wrapY){this.imgElement.style.backgroundRepeat="repeat-x"}else{if(!this.parameters.wrapX&&this.parameters.wrapY){this.imgElement.style.backgroundRepeat="repeat-y"}else{if(this.parameters.wrapX&&this.parameters.wrapY){this.imgElement.style.backgroundRepeat="repeat"}}}}}this.addLayer(new bigshot.HTMLDivElementLayer(this,this.imgElement,this.parameters.width,this.parameters.height,this.parameters.wrapX,this.parameters.wrapY))}};bigshot.Object.extend(bigshot.SimpleImage,bigshot.ImageBase);bigshot.FileSystem=function(){};bigshot.FileSystem.prototype={getFilename:function(a){},getImageFilename:function(b,a,c){},setPrefix:function(a){},getDescriptor:function(){},getPosterFilename:function(){}};bigshot.setupFileSystem=function(a){if(!a.fileSystem){if(a.fileSystemType=="archive"){a.fileSystem=new bigshot.ArchiveFileSystem(a)}else{if(a.fileSystemType=="dzi"){a.fileSystem=new bigshot.DeepZoomImageFileSystem(a)}else{if(a.fileSystemType=="simple"){a.fileSystem=new bigshot.SimpleFileSystem(a)}else{a.fileSystem=new bigshot.FolderFileSystem(a)}}}}};bigshot.SimpleFileSystem=function(a){this.parameters=a};bigshot.SimpleFileSystem.prototype={getDescriptor:function(){return{}},getPosterFilename:function(){return null},getFilename:function(a){return null},getImageFilename:function(b,a,c){return null},getPrefix:function(){return""},setPrefix:function(a){this.prefix=a}};bigshot.Object.validate("bigshot.SimpleFileSystem",bigshot.FileSystem);bigshot.FolderFileSystem=function(a){this.prefix=null;this.suffix="";this.parameters=a};bigshot.FolderFileSystem.prototype={getDescriptor:function(){this.browser=new bigshot.Browser();var b=this.browser.createXMLHttpRequest();b.open("GET",this.getFilename("descriptor"),false);b.send(null);var c={};if(b.status==200){var d=b.responseText.split(":");for(var a=0;a<d.length;a+=2){if(d[a]=="suffix"){c[d[a]]=d[a+1]}else{c[d[a]]=parseInt(d[a+1])}}this.suffix=c.suffix;return c}else{throw new Error("Unable to find descriptor.")}},getPosterFilename:function(){return this.getFilename("poster"+this.suffix)},setPrefix:function(a){this.prefix=a},getPrefix:function(){if(this.prefix){return this.prefix+"/"}else{return""}},getFilename:function(a){return this.parameters.basePath+"/"+this.getPrefix()+a},getImageFilename:function(c,b,d){var a=(-d)+"/"+c+"_"+b+this.suffix;return this.getFilename(a)}};bigshot.Object.validate("bigshot.FolderFileSystem",bigshot.FileSystem);bigshot.DeepZoomImageFileSystem=function(a){this.prefix="";this.suffix="";this.DZ_NAMESPACE="http://schemas.microsoft.com/deepzoom/2009";this.fullZoomLevel=0;this.posterName="";this.parameters=a};bigshot.DeepZoomImageFileSystem.prototype={getDescriptor:function(){var e={};var b=this.parameters.dataLoader.loadXml(this.parameters.basePath+this.prefix+".xml",false);var d=b.getElementsByTagName("Image")[0];var c=b.getElementsByTagName("Size")[0];e.width=parseInt(c.getAttribute("Width"));e.height=parseInt(c.getAttribute("Height"));e.tileSize=parseInt(d.getAttribute("TileSize"));e.overlap=parseInt(d.getAttribute("Overlap"));e.suffix="."+d.getAttribute("Format");e.posterSize=e.tileSize;this.suffix=e.suffix;this.fullZoomLevel=Math.ceil(Math.log(Math.max(e.width,e.height))/Math.LN2);e.minZoom=-this.fullZoomLevel;var a=Math.ceil(Math.log(e.tileSize)/Math.LN2);this.posterName=this.getImageFilename(0,0,a-this.fullZoomLevel);return e},setPrefix:function(a){this.prefix=a},getPosterFilename:function(){return this.posterName},getFilename:function(a){return this.parameters.basePath+this.prefix+"/"+a},getImageFilename:function(d,c,e){var a=this.fullZoomLevel+e;var b=a+"/"+d+"_"+c+this.suffix;return this.getFilename(b)}};bigshot.ArchiveFileSystem=function(d){this.indexSize=0;this.offset=0;this.index={};this.prefix="";this.suffix="";this.parameters=d;var a=new bigshot.Browser();var c=a.createXMLHttpRequest();c.open("GET",this.parameters.basePath+"&start=0&length=24&type=text/plain",false);c.send(null);if(c.status==200){if(c.responseText.substring(0,7)!="BIGSHOT"){alert('"'+this.parameters.basePath+'" is not a valid bigshot file');return}this.indexSize=parseInt(c.responseText.substring(8),16);this.offset=this.indexSize+24;c.open("GET",this.parameters.basePath+"&type=text/plain&start=24&length="+this.indexSize,false);c.send(null);if(c.status==200){var e=c.responseText.split(":");for(var b=0;b<e.length;b+=3){this.index[e[b]]={start:parseInt(e[b+1])+this.offset,length:parseInt(e[b+2])}}}else{alert('The index of "'+this.parameters.basePath+'" could not be loaded: '+c.status)}}else{alert('The header of "'+this.parameters.basePath+'" could not be loaded: '+c.status)}};bigshot.ArchiveFileSystem.prototype={getDescriptor:function(){this.browser=new bigshot.Browser();var b=this.browser.createXMLHttpRequest();b.open("GET",this.getFilename("descriptor"),false);b.send(null);var c={};if(b.status==200){var d=b.responseText.split(":");for(var a=0;a<d.length;a+=2){if(d[a]=="suffix"){c[d[a]]=d[a+1]}else{c[d[a]]=parseInt(d[a+1])}}this.suffix=c.suffix;return c}else{throw new Error("Unable to find descriptor.")}},getPosterFilename:function(){return this.getFilename("poster"+this.suffix)},getFilename:function(a){a=this.getPrefix()+a;if(!this.index[a]&&console){console.log("Can't find "+a)}var b=this.parameters.basePath+"&start="+this.index[a].start+"&length="+this.index[a].length;if(a.substring(a.length-4)==".jpg"){b=b+"&type=image/jpeg"}else{if(a.substring(a.length-4)==".png"){b=b+"&type=image/png"}else{b=b+"&type=text/plain"}}return b},getImageFilename:function(c,b,d){var a=(-d)+"/"+c+"_"+b+this.suffix;return this.getFilename(a)},getPrefix:function(){if(this.prefix){return this.prefix+"/"}else{return""}},setPrefix:function(a){this.prefix=a}};bigshot.Object.validate("bigshot.ArchiveFileSystem",bigshot.FileSystem);bigshot.VRTileCache=function(){};bigshot.VRTileCache.prototype={getTexture:function(b,a,c){},purge:function(){},dispose:function(){}};bigshot.ImageVRTileCache=function(c,a,b){this.imageTileCache=new bigshot.ImageTileCache(c,a,b);this.imageTileCache.setMaxTiles(999999,999999)};bigshot.ImageVRTileCache.prototype={getTexture:function(c,b,d){var a=this.imageTileCache.getImage(c,b,d);return a},purge:function(){this.imageTileCache.resetUsed()},dispose:function(){}};bigshot.Object.validate("bigshot.ImageVRTileCache",bigshot.VRTileCache);bigshot.TextureTileCache=function(d,a,c,b){this.parameters=c;this.webGl=b;this.fullImage=c.dataLoader.loadImage(c.fileSystem.getPosterFilename(),a);this.maxTextureCacheSize=512;this.maxImageCacheSize=2048;this.cachedTextures={};this.cachedImages={};this.requestedImages={};this.lastOnLoadFiredAt=0;this.imageRequests=0;this.partialImageSize=c.tileSize/8;this.imageLruMap=new bigshot.LRUMap();this.textureLruMap=new bigshot.LRUMap();this.onLoaded=d;this.browser=new bigshot.Browser();this.disposed=false};bigshot.TextureTileCache.prototype={getPartialTexture:function(b,o,e){if(this.fullImage.complete){var c=document.createElement("canvas");if(!c.width){return null}c.width=this.partialImageSize;c.height=this.partialImageSize;var n=c.getContext("2d");var p=this.parameters.posterSize/Math.max(this.parameters.width,this.parameters.height);var l=Math.floor(p*this.parameters.width);var d=Math.floor(p*this.parameters.height);var m=p*(this.parameters.tileSize-this.parameters.overlap)/Math.pow(2,e);var j=Math.floor(m*b);var i=Math.floor(m*o);var k=Math.floor(m);var f=Math.floor(m);var a=this.partialImageSize+2;var g=this.partialImageSize+2;if(j+k>l){k=l-j;a=this.partialImageSize*(k/Math.floor(m))}if(i+f>d){f=d-i;g=this.partialImageSize*(f/Math.floor(m))}n.drawImage(this.fullImage,j,i,k,f,-1,-1,a,g);return this.webGl.createImageTextureFromImage(c,this.parameters.textureMinFilter,this.parameters.textureMagFilter)}else{return null}},setCachedTexture:function(b,a){if(this.cachedTextures[b]!=null){this.webGl.deleteTexture(this.cachedTextures[b])}this.cachedTextures[b]=a},getTexture:function(d,c,e){var b=this.getImageKey(d,c,e);this.textureLruMap.access(b);this.imageLruMap.access(b);if(this.cachedTextures[b]){return this.cachedTextures[b]}else{if(this.cachedImages[b]){this.setCachedTexture(b,this.webGl.createImageTextureFromImage(this.cachedImages[b],this.parameters.textureMinFilter,this.parameters.textureMagFilter));return this.cachedTextures[b]}else{this.requestImage(d,c,e);var a=this.getPartialTexture(d,c,e);if(a){this.setCachedTexture(b,a)}return a}}},requestImage:function(d,c,e){var a=this.getImageKey(d,c,e);if(!this.requestedImages[a]){this.imageRequests++;var b=this;this.parameters.dataLoader.loadImage(this.getImageFilename(d,c,e),function(g){if(b.disposed){return}b.cachedImages[a]=g;b.setCachedTexture(a,b.webGl.createImageTextureFromImage(g,b.parameters.textureMinFilter,b.parameters.textureMagFilter));delete b.requestedImages[a];b.imageRequests--;var f=new Date();if(b.imageRequests==0||f.getTime()>(b.lastOnLoadFiredAt+50)){b.lastOnLoadFiredAt=f.getTime();b.onLoaded()}});this.requestedImages[a]=true}},purge:function(){var a=this;this.purgeCache(this.textureLruMap,this.cachedTextures,this.maxTextureCacheSize,function(b){a.webGl.deleteTexture(a.cachedTextures[b])});this.purgeCache(this.imageLruMap,this.cachedImages,this.maxImageCacheSize,function(b){})},purgeCache:function(b,c,a,e){for(var d=0;d<64;++d){if(b.getSize()>a){var f=b.leastUsed();b.remove(f);if(e){e(f)}delete c[f]}else{break}}},getImageKey:function(b,a,c){return"I"+b+"_"+a+"_"+c},getImageFilename:function(c,a,d){var b=this.parameters.fileSystem.getImageFilename(c,a,d);return b},dispose:function(){this.disposed=true;for(var a in this.cachedTextures){this.webGl.deleteTexture(this.cachedTextures[a])}}};bigshot.Object.validate("bigshot.TextureTileCache",bigshot.VRTileCache);bigshot.VRFace=function(b,j,g,d,m,l,e){var i=this;this.owner=b;this.key=j;this.topLeft=g;this.width=d;this.u=m;this.v=l;this.updated=false;this.parameters=new Object();for(var f in this.owner.getParameters()){this.parameters[f]=this.owner.getParameters()[f]}bigshot.setupFileSystem(this.parameters);this.parameters.fileSystem.setPrefix("face_"+j);this.parameters.merge(this.parameters.fileSystem.getDescriptor(),false);this.tileCache=b.renderer.createTileCache(function(){i.updated=true;b.renderUpdated(bigshot.VRPanorama.ONRENDER_TEXTURE_UPDATE)},e,this.parameters);this.fullSize=this.parameters.width;this.overlap=this.parameters.overlap;this.tileSize=this.parameters.tileSize;this.minDivisions=0;var c=Math.log(this.fullSize-this.overlap)/Math.LN2;var a=Math.log(this.tileSize-this.overlap)/Math.LN2;this.maxDivisions=Math.floor(c-a);this.maxTesselation=this.parameters.maxTesselation>=0?this.parameters.maxTesselation:this.maxDivisions};bigshot.VRFace.prototype={browser:new bigshot.Browser(),dispose:function(){this.tileCache.dispose()},pt3dMultAdd:function(d,b,c){return{x:d.x*b+c.x,y:d.y*b+c.y,z:d.z*b+c.z}},pt3dMult:function(b,a){return{x:b.x*a,y:b.y*a,z:b.z*a}},generateFace:function(g,e,d,b,a,c){d*=this.tileSize/(this.tileSize-this.overlap);var f=this.tileCache.getTexture(b,a,-this.maxDivisions+c);g.addQuad(this.owner.renderer.createTexturedQuad(e,this.pt3dMult(this.u,d),this.pt3dMult(this.v,d),f))},VISIBLE_NONE:0,VISIBLE_SOME:1,VISIBLE_ALL:2,pointInRect:function(b,c,a){return(b.x>=c.x&&b.y>=c.y&&b.x<a.x&&b.y<a.y)},intersectWithView:function intersectWithView(x){var d=0;var c=[];var m=x.length;for(var r=0;r<m;++r){if(x[r]==null){d++}else{c.push(x[r])}}if(d==4){return this.VISIBLE_NONE}var v=c[0].x;var t=c[0].y;var u=v;var s=t;var l=0;var j=0;var k=this.viewportWidth;var g=this.viewportHeight;var a=0;var b=c.length;for(var r=1;r<b;++r){var f=c[r].x;var e=c[r].y;v=v<f?v:f;t=t<e?t:e;u=u>f?u:f;s=s>e?s:e}var q=v>l?v:l;var p=t>j?t:j;var o=u<k?u:k;var n=s<g?s:g;if(q<=o&&p<=n){return this.VISIBLE_SOME}return this.VISIBLE_NONE},screenDistance:function screenDistance(b,a){if(b==null||a==null){return 0}return Math.max(Math.abs(b.x-a.x),Math.abs(b.y-a.y))},transformToScreen:function transformToScreen(a){return this.owner.renderer.transformToScreen(a)},generateSubdivisionFace:function generateSubdivisionFace(p,m,l,b,u,s,y){if(!y){y=new Array(4);y[0]=this.transformToScreen(m);var k=this.pt3dMultAdd(this.u,l,m);y[1]=this.transformToScreen(k);var n=this.pt3dMultAdd(this.v,l,m);y[3]=this.transformToScreen(n);var e=this.pt3dMultAdd(this.v,l,k);y[2]=this.transformToScreen(e)}var x=this.intersectWithView(y);if(x==this.VISIBLE_NONE){return}var j=0;for(var o=0;o<y.length;++o){var g=(o+1)%4;j=Math.max(this.screenDistance(y[o],y[g]),j)}j*=this.owner.browser.getDevicePixelScale();if(b<this.minDivisions||((j>this.owner.maxTextureMagnification*(this.tileSize-this.overlap))&&b<this.maxDivisions&&b<this.maxTesselation)){var t=this.pt3dMultAdd({x:this.u.x+this.v.x,y:this.u.y+this.v.y,z:this.u.z+this.v.z},l/2,m);var r=this.pt3dMultAdd(this.u,l/2,m);var f=this.pt3dMultAdd(this.v,l/2,m);var q=this.transformToScreen(t);var v=this.transformToScreen(f);var d=this.transformToScreen(r);var c=this.transformToScreen(this.pt3dMultAdd(this.u,l,f));var a=this.transformToScreen(this.pt3dMultAdd(this.v,l,r));this.generateSubdivisionFace(p,m,l/2,b+1,u*2,s*2,[y[0],d,q,v]);this.generateSubdivisionFace(p,r,l/2,b+1,u*2+1,s*2,[d,y[1],c,q]);this.generateSubdivisionFace(p,f,l/2,b+1,u*2,s*2+1,[v,q,a,y[3]]);this.generateSubdivisionFace(p,t,l/2,b+1,u*2+1,s*2+1,[q,c,y[2],a])}else{this.generateFace(p,m,l,u,s,b)}},isUpdated:function(){return this.updated},render:function(a){this.updated=false;this.viewportWidth=this.owner.renderer.getViewportWidth();this.viewportHeight=this.owner.renderer.getViewportHeight();this.generateSubdivisionFace(a,this.topLeft,this.width,0,0,0)},endRender:function(){this.tileCache.purge()}};bigshot.WebGLUtil={debug:false,contextNames:["webgl","experimental-webgl"],createContext0:function(a,b){var c=this.debug?WebGLDebugUtils.makeDebugContext(a.getContext(b)):a.getContext(b);return c},createContext:function(a){for(var b=0;b<this.contextNames.length;++b){try{var d=this.createContext0(a,this.contextNames[b]);if(d){return d}}catch(c){}}throw new Error("Could not initialize WebGL.")},isWebGLSupported:function(){var a=document.createElement("canvas");if(!a.width){return false}try{this.createContext(a);return true}catch(b){return false}}};bigshot.TransformStack=function(){this.mvMatrix=null;this.mvMatrixStack=[];this.reset()};bigshot.TransformStack.prototype={push:function(a){if(a){this.mvMatrixStack.push(a.dup());this.mvMatrix=a.dup();return mvMatrix}else{this.mvMatrixStack.push(this.mvMatrix.dup());return mvMatrix}},pop:function(){if(this.mvMatrixStack.length==0){throw new Error("Invalid popMatrix!")}this.mvMatrix=this.mvMatrixStack.pop();return mvMatrix},reset:function(){this.mvMatrix=Matrix.I(4)},multiply:function(a){this.mvMatrix=a.x(this.mvMatrix)},translate:function(b){var a=Matrix.Translation($V([b.x,b.y,b.z])).ensure4x4();this.multiply(a)},rotate:function(c,b){var d=c*Math.PI/180;var a=Matrix.Rotation(d,$V([b.x,b.y,b.z])).ensure4x4();this.multiply(a)},rotateX:function(a){this.rotate(a,{x:1,y:0,z:0})},rotateY:function(a){this.rotate(a,{x:0,y:1,z:0})},rotateZ:function(a){this.rotate(a,{x:0,y:0,z:1})},perspective:function(d,c,b,e){var a=makePerspective(d,c,b,e);this.multiply(a)},matrix:function(){return this.mvMatrix}};bigshot.WebGL=function(a){this.canvas=a;this.gl=bigshot.WebGLUtil.createContext(this.canvas);this.mvMatrix=new bigshot.TransformStack();this.pMatrix=new bigshot.TransformStack();this.shaderProgram=null;this.onresize()};bigshot.WebGL.prototype={onresize:function(){this.gl.viewportWidth=this.canvas.width;this.gl.viewportHeight=this.canvas.height},fragmentShader:"#ifdef GL_ES\n precision highp float;\n#endif\n\nvarying vec2 vTextureCoord;\n\nuniform sampler2D uSampler;\n\nvoid main(void) {\n gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n}\n",vertexShader:"attribute vec3 aVertexPosition;\nattribute vec2 aTextureCoord;\n\nuniform mat4 uMVMatrix;\nuniform mat4 uPMatrix;\n\nvarying vec2 vTextureCoord;\n\nvoid main(void) {\n gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n vTextureCoord = aTextureCoord;\n}",createShader:function(c,a){var b=this.gl.createShader(a);this.gl.shaderSource(b,c);this.gl.compileShader(b);if(!this.gl.getShaderParameter(b,this.gl.COMPILE_STATUS)){alert(this.gl.getShaderInfoLog(b));return null}return b},createFragmentShader:function(a){return this.createShader(a,this.gl.FRAGMENT_SHADER)},createVertexShader:function(a){return this.createShader(a,this.gl.VERTEX_SHADER)},initShaders:function(){this.shaderProgram=this.gl.createProgram();this.gl.attachShader(this.shaderProgram,this.createVertexShader(this.vertexShader));this.gl.attachShader(this.shaderProgram,this.createFragmentShader(this.fragmentShader));this.gl.linkProgram(this.shaderProgram);if(!this.gl.getProgramParameter(this.shaderProgram,this.gl.LINK_STATUS)){throw new Error("Could not initialise shaders");return}this.gl.useProgram(this.shaderProgram);this.shaderProgram.vertexPositionAttribute=this.gl.getAttribLocation(this.shaderProgram,"aVertexPosition");this.gl.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute);this.shaderProgram.textureCoordAttribute=this.gl.getAttribLocation(this.shaderProgram,"aTextureCoord");this.gl.enableVertexAttribArray(this.shaderProgram.textureCoordAttribute);this.shaderProgram.pMatrixUniform=this.gl.getUniformLocation(this.shaderProgram,"uPMatrix");this.shaderProgram.mvMatrixUniform=this.gl.getUniformLocation(this.shaderProgram,"uMVMatrix");this.shaderProgram.samplerUniform=this.gl.getUniformLocation(this.shaderProgram,"uSampler")},setMatrixUniforms:function(){this.gl.uniformMatrix4fv(this.shaderProgram.pMatrixUniform,false,new Float32Array(this.pMatrix.matrix().flatten()));this.gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform,false,new Float32Array(this.mvMatrix.matrix().flatten()))},createImageTextureFromImage:function(d,a,c){var b=this.gl.createTexture();this.handleImageTextureLoaded(this,b,d,a,c);return b},createImageTextureFromSource:function(e,a,d){var f=new Image();var c=this.gl.createTexture();var b=this;f.onload=function(){b.handleImageTextureLoaded(b,c,f,a,d)};f.src=e;return c},handleImageTextureLoaded:function(c,b,e,a,d){c.gl.bindTexture(c.gl.TEXTURE_2D,b);c.gl.texImage2D(c.gl.TEXTURE_2D,0,c.gl.RGBA,c.gl.RGBA,c.gl.UNSIGNED_BYTE,e);c.gl.texParameteri(c.gl.TEXTURE_2D,c.gl.TEXTURE_MAG_FILTER,d?d:c.gl.NEAREST);c.gl.texParameteri(c.gl.TEXTURE_2D,c.gl.TEXTURE_MIN_FILTER,a?a:c.gl.NEAREST);c.gl.texParameteri(c.gl.TEXTURE_2D,c.gl.TEXTURE_WRAP_S,c.gl.CLAMP_TO_EDGE);c.gl.texParameteri(c.gl.TEXTURE_2D,c.gl.TEXTURE_WRAP_T,c.gl.CLAMP_TO_EDGE);if(a==c.gl.NEAREST_MIPMAP_NEAREST||a==c.gl.LINEAR_MIPMAP_NEAREST||a==c.gl.NEAREST_MIPMAP_LINEAR||a==c.gl.LINEAR_MIPMAP_LINEAR){c.gl.generateMipmap(c.gl.TEXTURE_2D)}c.gl.bindTexture(c.gl.TEXTURE_2D,null)},deleteTexture:function(a){this.gl.deleteTexture(a)},dispose:function(){delete this.canvas;delete this.gl}};bigshot.VRRenderer=function(){};bigshot.VRRenderer.prototype={createTileCache:function(c,a,b){},createTexturedQuadScene:function(){},createTexturedQuad:function(d,b,a,c){},getViewportWidth:function(){},getViewportHeight:function(){},transformToWorld:function(a){},transformWorldToScreen:function(a){},transformToScreen:function(a){},dispose:function(){},beginRender:function(b,a,d,c){},endRender:function(){},onresize:function(){},resize:function(a,b){},getElement:function(){}};bigshot.AbstractVRRenderer=function(){};bigshot.AbstractVRRenderer.prototype={transformToWorld:function transformToWorld(a){var b=this.mvMatrix.matrix().xPoint3Dhom1(a);return b},transformWorldToScreen:function transformWorldToScreen(d){if(d.z>0){return null}var a=this.pMatrix.matrix().xPoint3Dhom(d);if(Math.abs(a.w)<Sylvester.precision){return null}var i=a.x;var f=a.y;var e=a.z;var c=this.getViewportWidth();var g=this.getViewportHeight();var b={x:(c/2)*i/e+c/2,y:-(g/2)*f/e+g/2};return b},transformToScreen:function transformToScreen(a){var d=this.mvpMatrix.xPoint3Dhom(a);if(d.z<0){return null}var e=d.w;if(Math.abs(d.w)<Sylvester.precision){return null}var i=d.x;var f=d.y;var c=this.getViewportWidth();var g=this.getViewportHeight();var b={x:(c/2)*i/e+c/2,y:-(g/2)*f/e+g/2};return b}};bigshot.CSS3DVRRenderer=function(a){this.container=a;this.canvasOrigin=document.createElement("div");this.canvasOrigin.style.WebkitTransformOrigin="0px 0px 0px";this.canvasOrigin.style.WebkitTransformStyle="preserve-3d";this.canvasOrigin.style.WebkitPerspective="600px";this.canvasOrigin.style.position="relative";this.canvasOrigin.style.left="50%";this.canvasOrigin.style.top="50%";this.container.appendChild(this.canvasOrigin);this.viewport=document.createElement("div");this.viewport.style.WebkitTransformOrigin="0px 0px 0px";this.viewport.style.WebkitTransformStyle="preserve-3d";this.canvasOrigin.appendChild(this.viewport);this.world=document.createElement("div");this.world.style.WebkitTransformOrigin="0px 0px 0px";this.world.style.WebkitTransformStyle="preserve-3d";this.viewport.appendChild(this.world);this.browser.removeAllChildren(this.world);this.view=null;this.mvMatrix=new bigshot.TransformStack();this.yaw=0;this.pitch=0;this.fov=0;this.pMatrix=new bigshot.TransformStack();this.onresize=function(){};this.viewportSize=null};bigshot.CSS3DVRRenderer.prototype={browser:new bigshot.Browser(),dispose:function(){},createTileCache:function(c,a,b){return new bigshot.ImageVRTileCache(c,a,b)},createTexturedQuadScene:function(){return new bigshot.CSS3DTexturedQuadScene(this.world,128,this.view)},createTexturedQuad:function(d,b,a,c){return new bigshot.CSS3DTexturedQuad(d,b,a,c)},getElement:function(){return this.container},supportsUpdate:function(){return false},getViewportWidth:function(){if(this.viewportSize){return this.viewportSize.w}return this.browser.getElementSize(this.container).w},getViewportHeight:function(){if(this.viewportSize){return this.viewportSize.h}return this.browser.getElementSize(this.container).h},onresize:function(){},resize:function(a,b){if(this.container.style.width!=""){this.container.style.width=a+"px"}if(this.container.style.height!=""){this.container.style.height=b+"px"}},beginRender:function(e,c,j,g){this.viewportSize=this.browser.getElementSize(this.container);this.yaw=e.y;this.pitch=e.p;this.fov=c;var b=0.5*c*Math.PI/180;var a=this.getViewportHeight()/2;var f=a/Math.tan(b);this.mvMatrix.reset();this.view=j;this.mvMatrix.translate(this.view);this.mvMatrix.rotateZ(g.r);this.mvMatrix.rotateX(g.p);this.mvMatrix.rotateY(g.y);this.mvMatrix.rotateY(this.yaw);this.mvMatrix.rotateX(this.pitch);this.pMatrix.reset();this.pMatrix.perspective(this.fov,this.getViewportWidth()/this.getViewportHeight(),0.1,100);this.mvpMatrix=this.pMatrix.matrix().multiply(this.mvMatrix.matrix());this.canvasOrigin.style.WebkitPerspective=f+"px";for(var d=this.world.children.length-1;d>=0;--d){this.world.children[d].inWorld=1}this.world.style.WebkitTransform="rotate3d(1,0,0,"+(-e.p)+"deg) rotate3d(0,1,0,"+e.y+"deg) rotate3d(0,1,0,"+(g.y)+"deg) rotate3d(1,0,0,"+(-g.p)+"deg) rotate3d(0,0,1,"+(-g.r)+"deg) ";this.world.style.WebkitTransformStyle="preserve-3d";this.world.style.WebKitBackfaceVisibility="hidden";this.viewport.style.WebkitTransform="translateZ("+f+"px)"},endRender:function(){for(var a=this.world.children.length-1;a>=0;--a){var b=this.world.children[a];if(!b.inWorld||b.inWorld!=2){delete b.inWorld;this.world.removeChild(b)}}this.viewportSize=null}};bigshot.Object.extend(bigshot.CSS3DVRRenderer,bigshot.AbstractVRRenderer);bigshot.Object.validate("bigshot.CSS3DVRRenderer",bigshot.VRRenderer);bigshot.CSS3DTexturedQuad=function(d,b,a,c){this.p=d;this.u=b;this.v=a;this.image=c};bigshot.CSS3DTexturedQuad.prototype={crossProduct:function crossProduct(d,c){return{x:d.y*c.z-d.z*c.y,y:d.z*c.x-d.x*c.z,z:d.x*c.y-d.y*c.x}},vecToStr:function vecToStr(a){return(a.x)+","+(a.y)+","+(a.z)},quadTransform:function quadTransform(c,d,b){var a=this.crossProduct(d,b);var e="matrix3d("+this.vecToStr(d)+",0,"+this.vecToStr(b)+",0,"+this.vecToStr(a)+",0,"+this.vecToStr(c)+",1)";return e},norm:function norm(a){return Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z)},render:function render(e,i,a){var d=i/(this.image.width-1);var g=i*1;var f=this.p;var c=this.u;var b=this.v;this.image.style.position="absolute";if(!this.image.inWorld||this.image.inWorld!=1){e.appendChild(this.image)}this.image.inWorld=2;this.image.style.WebkitTransformOrigin="0px 0px 0px";this.image.style.WebkitTransform=this.quadTransform({x:(f.x+a.x)*g,y:(-f.y+a.y)*g,z:(f.z+a.z)*g},{x:c.x*d,y:-c.y*d,z:c.z*d},{x:b.x*d,y:-b.y*d,z:b.z*d})}};bigshot.CSS3DTexturedQuadScene=function(b,c,a){this.quads=new Array();this.world=b;this.scale=c;this.view=a};bigshot.CSS3DTexturedQuadScene.prototype={addQuad:function(a){this.quads.push(a)},render:function(){for(var a=0;a<this.quads.length;++a){this.quads[a].render(this.world,this.scale,this.view)}}};bigshot.TexturedQuadScene=function(){};bigshot.TexturedQuadScene.prototype={addQuad:function(a){},render:function(){}};bigshot.WebGLVRRenderer=function(a){this.container=a;this.canvas=document.createElement("canvas");this.canvas.width=480;this.canvas.height=480;this.canvas.style.position="absolute";this.container.appendChild(this.canvas);this.webGl=new bigshot.WebGL(this.canvas);this.webGl.initShaders();this.webGl.gl.clearColor(0,0,0,1);this.webGl.gl.blendFunc(this.webGl.gl.ONE,this.webGl.gl.ZERO);this.webGl.gl.enable(this.webGl.gl.BLEND);this.webGl.gl.disable(this.webGl.gl.DEPTH_TEST);this.webGl.gl.clearDepth(1);var b=this;this.buffers=new bigshot.TimedWeakReference(function(){return b.setupBuffers()},function(c){b.disposeBuffers(c)},1000)};bigshot.WebGLVRRenderer.prototype={createTileCache:function(c,a,b){return new bigshot.TextureTileCache(c,a,b,this.webGl)},createTexturedQuadScene:function(){return new bigshot.WebGLTexturedQuadScene(this.webGl,this.buffers)},setupBuffers:function(){var c=this.webGl.gl.createBuffer();var a=this.webGl.gl.createBuffer();this.webGl.gl.bindBuffer(this.webGl.gl.ARRAY_BUFFER,a);var d=[0,0,1,0,1,1,0,1];this.webGl.gl.bufferData(this.webGl.gl.ARRAY_BUFFER,new Float32Array(d),this.webGl.gl.STATIC_DRAW);var b=this.webGl.gl.createBuffer();this.webGl.gl.bindBuffer(this.webGl.gl.ELEMENT_ARRAY_BUFFER,b);var e=[0,2,1,0,3,2];this.webGl.gl.bufferData(this.webGl.gl.ELEMENT_ARRAY_BUFFER,new Uint16Array(e),this.webGl.gl.STATIC_DRAW);this.webGl.gl.bindBuffer(this.webGl.gl.ARRAY_BUFFER,a);this.webGl.gl.vertexAttribPointer(this.webGl.shaderProgram.textureCoordAttribute,2,this.webGl.gl.FLOAT,false,0,0);this.webGl.gl.bindBuffer(this.webGl.gl.ARRAY_BUFFER,c);this.webGl.gl.vertexAttribPointer(this.webGl.shaderProgram.vertexPositionAttribute,3,this.webGl.gl.FLOAT,false,0,0);return{vertexPositionBuffer:c,textureCoordBuffer:a,vertexIndexBuffer:b}},dispose:function(){this.buffers.dispose();this.container.removeChild(this.canvas);delete this.canvas;this.webGl.dispose();delete this.webGl},disposeBuffers:function(a){this.webGl.gl.deleteBuffer(a.vertexPositionBuffer);this.webGl.gl.deleteBuffer(a.vertexIndexBuffer);this.webGl.gl.deleteBuffer(a.textureCoordBuffer)},getElement:function(){return this.canvas},supportsUpdate:function(){return false},createTexturedQuad:function(d,b,a,c){return new bigshot.WebGLTexturedQuad(d,b,a,c)},getViewportWidth:function(){return this.webGl.gl.viewportWidth},getViewportHeight:function(){return this.webGl.gl.viewportHeight},beginRender:function(b,a,d,c){this.webGl.gl.viewport(0,0,this.webGl.gl.viewportWidth,this.webGl.gl.viewportHeight);this.webGl.pMatrix.reset();this.webGl.pMatrix.perspective(a,this.webGl.gl.viewportWidth/this.webGl.gl.viewportHeight,0.1,100);this.webGl.mvMatrix.reset();this.webGl.mvMatrix.translate(d);this.webGl.mvMatrix.rotateZ(c.r);this.webGl.mvMatrix.rotateX(c.p);this.webGl.mvMatrix.rotateY(c.y);this.webGl.mvMatrix.rotateY(b.y);this.webGl.mvMatrix.rotateX(b.p);this.mvMatrix=this.webGl.mvMatrix;this.pMatrix=this.webGl.pMatrix;this.mvpMatrix=this.pMatrix.matrix().multiply(this.mvMatrix.matrix())},endRender:function(){},resize:function(a,b){this.canvas.width=a;this.canvas.height=b;if(this.container.style.width!=""){this.container.style.width=a+"px"}if(this.container.style.height!=""){this.container.style.height=b+"px"}},onresize:function(){this.webGl.onresize()}};bigshot.Object.extend(bigshot.WebGLVRRenderer,bigshot.AbstractVRRenderer);bigshot.Object.validate("bigshot.WebGLVRRenderer",bigshot.VRRenderer);bigshot.TexturedQuad=function(){};bigshot.WebGLTexturedQuad=function(d,b,a,c){this.p=d;this.u=b;this.v=a;this.texture=c};bigshot.WebGLTexturedQuad.prototype={render:function(e,d,a,c){e.gl.bindBuffer(e.gl.ARRAY_BUFFER,d);var b=[this.p.x,this.p.y,this.p.z,this.p.x+this.u.x,this.p.y+this.u.y,this.p.z+this.u.z,this.p.x+this.u.x+this.v.x,this.p.y+this.u.y+this.v.y,this.p.z+this.u.z+this.v.z,this.p.x+this.v.x,this.p.y+this.v.y,this.p.z+this.v.z];e.gl.bufferData(e.gl.ARRAY_BUFFER,new Float32Array(b),e.gl.STATIC_DRAW);e.gl.activeTexture(e.gl.TEXTURE0);e.gl.bindTexture(e.gl.TEXTURE_2D,this.texture);e.gl.uniform1i(e.shaderProgram.samplerUniform,0);e.gl.bindBuffer(e.gl.ELEMENT_ARRAY_BUFFER,c);e.gl.drawElements(e.gl.TRIANGLES,6,e.gl.UNSIGNED_SHORT,0);e.gl.bindTexture(e.gl.TEXTURE_2D,null)}};bigshot.WebGLTexturedQuadScene=function(b,a){this.quads=new Array();this.webGl=b;this.buffers=a};bigshot.WebGLTexturedQuadScene.prototype={addQuad:function(a){this.quads.push(a)},render:function(){var a=this.buffers.get();var f=a.vertexPositionBuffer;var c=a.textureCoordBuffer;var e=a.vertexIndexBuffer;this.webGl.setMatrixUniforms();for(var d=0;d<this.quads.length;++d){this.quads[d].render(this.webGl,f,c,e)}}};bigshot.VRPanoramaParameters=function(b){this.posterSize=0;this.emptyImage=null;this.suffix=null;this.width=0;this.height=0;this.container=null;this.maxTesselation=-1;this.tileSize=0;this.overlap=0;this.basePath=null;this.fileSystemType="folder";this.fileSystem=null;this.dataLoader=new bigshot.DefaultDataLoader();this.maxTextureMagnification=1;this.textureMagFilter=null;this.textureMinFilter=null;this.minFov=2;this.maxFov=90;this.minPitch=-90;this.maxPitch=90;this.minYaw=-360;this.maxYaw=720;this.yawOffset=0;this.pitchOffset=0;this.rollOffset=0;this.onload=null;this.renderer=null;this.fling=true;this.flingScale=0.004;if(b){for(var a in b){this[a]=b[a]}}this.merge=function(d,e){for(var c in d){if(e||!this[c]){this[c]=d[c]}}};return this};bigshot.VRPanorama=function(d){bigshot.EventDispatcher.call(this);var c=this;this.parameters=d;this.maxTextureMagnification=d.maxTextureMagnification;this.container=d.container;this.browser=new bigshot.Browser();this.dragStart=null;this.dragDistance=0;this.hotspots=[];this.disposed=false;this.transformOffsets={y:d.yawOffset,p:d.pitchOffset,r:d.rollOffset};this.state={rotation:{p:0,y:0,r:0},fov:45,translation:{x:0,y:0,z:0}};this.renderer=null;if(this.parameters.renderer){if(this.parameters.renderer=="css"){this.renderer=new bigshot.CSS3DVRRenderer(this.container)}else{if(this.parameters.renderer=="webgl"){this.renderer=new bigshot.WebGLVRRenderer(this.container)}else{throw new Error("Unknown renderer: "+this.parameters.renderer)}}}else{this.renderer=bigshot.WebGLUtil.isWebGLSupported()?new bigshot.WebGLVRRenderer(this.container):new bigshot.CSS3DVRRenderer(this.container)}this.renderListeners=new Array();this.renderables=new Array();this.idleCounter=0;this.maxIdleCounter=-1;this.smoothrotatePermit=0;var f=function(g){if(g.preventDefault){g.preventDefault()}return false};this.fullScreenHandler=null;this.renderAsapPermitTaken=false;this.sizeContainer=null;var a={facesLeft:6,faceLoaded:function(){this.facesLeft--;if(this.facesLeft==0){if(c.parameters.onload){c.parameters.onload()}}}};var b=function(){a.faceLoaded()};this.vrFaces=new Array();this.vrFaces[0]=new bigshot.VRFace(this,"f",{x:-1,y:1,z:-1},2,{x:1,y:0,z:0},{x:0,y:-1,z:0},b);this.vrFaces[1]=new bigshot.VRFace(this,"b",{x:1,y:1,z:1},2,{x:-1,y:0,z:0},{x:0,y:-1,z:0},b);this.vrFaces[2]=new bigshot.VRFace(this,"l",{x:-1,y:1,z:1},2,{x:0,y:0,z:-1},{x:0,y:-1,z:0},b);this.vrFaces[3]=new bigshot.VRFace(this,"r",{x:1,y:1,z:-1},2,{x:0,y:0,z:1},{x:0,y:-1,z:0},b);this.vrFaces[4]=new bigshot.VRFace(this,"u",{x:-1,y:1,z:1},2,{x:1,y:0,z:0},{x:0,y:0,z:-1},b);this.vrFaces[5]=new bigshot.VRFace(this,"d",{x:-1,y:-1,z:-1},2,{x:1,y:0,z:0},{x:0,y:0,z:1},b);var e=function(g){if(g.clientX){return g}else{return{clientX:g.changedTouches[0].clientX,clientY:g.changedTouches[0].clientY}}};this.lastTouchStartAt=-1;this.allListeners={mousedown:function(g){c.smoothRotate();c.resetIdle();c.dragMouseDown(g);return f(g)},mouseup:function(g){c.resetIdle();c.dragMouseUp(g);return f(g)},mousemove:function(g){c.resetIdle();c.dragMouseMove(g);return f(g)},gesturestart:function(g){c.gestureStart(g);return f(g)},gesturechange:function(g){c.gestureChange(g);return f(g)},gestureend:function(g){c.gestureEnd(g);return f(g)},DOMMouseScroll:function(g){c.resetIdle();c.mouseWheel(g);return f(g)},mousewheel:function(g){c.resetIdle();c.mouseWheel(g);return f(g)},dblclick:function(g){c.mouseDoubleClick(g);return f(g)},touchstart:function(g){c.smoothRotate();c.lastTouchStartAt=new Date().getTime();c.resetIdle();c.dragMouseDown(e(g));return f(g)},touchend:function(i){c.resetIdle();var g=c.dragMouseUp(e(i));if(!g&&(c.lastTouchStartAt>new Date().getTime()-350)){c.mouseDoubleClick(e(i))}c.lastTouchStartAt=-1;return f(i)},touchmove:function(g){if(c.dragDistance>24){c.lastTouchStartAt=-1}c.resetIdle();c.dragMouseMove(e(g));return f(g)}};this.addEventListeners();this.onresizeHandler=function(g){c.onresize()};this.browser.registerListener(window,"resize",this.onresizeHandler,false);this.browser.registerListener(document.body,"orientationchange",this.onresizeHandler,false);this.setPitch(0);this.setYaw(0);this.setFov(45)};bigshot.VRPanorama.DRAG_GRAB="grab";bigshot.VRPanorama.DRAG_PAN="pan";bigshot.VRPanorama.ONRENDER_BEGIN=0;bigshot.VRPanorama.ONRENDER_END=1;bigshot.VRPanorama.ONRENDER_TEXTURE_UPDATE=0;bigshot.VRPanorama.prototype={addHotspot:function(a){this.hotspots.push(a)},getParameters:function(){return this.parameters},setTranslation:function(a,c,b){this.state.translation.x=a;this.state.translation.y=c;this.state.translation.z=b},getTranslation:function(){return this.state.translation},setFov:function(a){a=Math.min(this.parameters.maxFov,a);a=Math.max(this.parameters.minFov,a);this.state.fov=a},getFov:function(){return this.state.fov},screenToPolar:function(g,e){var c=this.screenToRayDelta(g,e);var f=$V([c.x,c.y,c.z,1]);f=Matrix.RotationX(this.getPitch()*Math.PI/180).ensure4x4().x(f);f=Matrix.RotationY(-this.getYaw()*Math.PI/180).ensure4x4().x(f);var l=f.e(1);var k=f.e(2);var j=f.e(3);var d=Math.sqrt(l*l+j*j);var a=Math.atan2(l,-j)*180/Math.PI;var i=Math.atan2(k,d)*180/Math.PI;var b={};b.yaw=(a+360)%360;b.pitch=i;return b},snapPitch:function(a){a=Math.min(this.parameters.maxPitch,a);a=Math.max(this.parameters.minPitch,a);return a},setPitch:function(a){this.state.rotation.p=this.snapPitch(a)},circleDistance:function(d,c){if(c>d){var b=(c-d);var a=((c-360)-d);return Math.abs(b)<Math.abs(a)?b:a}else{var b=(c-d);var a=(360-d)+c;return Math.abs(b)<Math.abs(a)?b:a}},circleSnapTo:function(d,e,c){var b=this.circleDistance(d,e);var a=this.circleDistance(d,c);return Math.abs(b)<Math.abs(a)?e:c},snapYaw:function(a){a%=360;if(a<0){a+=360}if(this.parameters.minYaw<this.parameters.maxYaw){if(a>this.parameters.maxYaw||a<this.parameters.minYaw){a=circleSnapTo(a,this.parameters.minYaw,this.parameters.maxYaw)}}else{if(a>this.parameters.minYaw){}else{if(a>this.parameters.maxYaw){a=circleSnapTo(a,this.parameters.minYaw,this.parameters.maxYaw)}else{}}}return a},setYaw:function(a){this.state.rotation.y=this.snapYaw(a)},getYaw:function(){return this.state.rotation.y},getPitch:function(){return this.state.rotation.p},dispose:function(){this.disposed=true;this.browser.unregisterListener(window,"resize",this.onresizeHandler,false);this.browser.unregisterListener(document.body,"orientationchange",this.onresizeHandler,false);this.removeEventListeners();for(var a=0;a<this.vrFaces.length;++a){this.vrFaces[a].dispose()}this.renderer.dispose()},createVREventData:function(b){var a=this.browser.getElementPosition(this.container);b.localX=b.clientX-a.x;b.localY=b.clientY-a.y;b.ray=this.screenToRay(b.localX,b.localY);var c=this.screenToPolar(b.localX,b.localY);b.yaw=c.yaw;b.pitch=c.pitch;b.target=this;b.currentTarget=this;return new bigshot.VREvent(b)},beginRender:function(b,a){this.onrender(bigshot.VRPanorama.ONRENDER_BEGIN,b,a);this.renderer.beginRender(this.state.rotation,this.state.fov,this.state.translation,this.transformOffsets)},addRenderListener:function(a){var b=new Array();b=b.concat(this.renderListeners);b.push(a);this.renderListeners=b},removeRenderListener:function(b){var c=new Array();c=c.concat(this.renderListeners);for(var a=0;a<c.length;++a){if(c[a]===b){c.splice(a,1);break}}this.renderListeners=c},onrender:function(d,c,b){var e=this.renderListeners;for(var a=0;a<e.length;++a){e[a](d,c,b)}},endRender:function(c,b){for(var a in this.vrFaces){this.vrFaces[a].endRender()}this.renderer.endRender();this.onrender(bigshot.VRPanorama.ONRENDER_END,c,b)},addRenderable:function(a){var b=new Array();b.concat(this.renderables);b.push(a);this.renderables=b},removeRenderable:function(b){var c=new Array();c.concat(this.renderables);for(var a=0;a<c.length;++a){if(c[a]==listener){c.splice(a,1);break}}this.renderables=c},render:function(d,c){if(!this.disposed){this.beginRender(d,c);var e=this.renderer.createTexturedQuadScene();for(var b in this.vrFaces){this.vrFaces[b].render(e)}for(var a=0;a<this.renderables.length;++a){this.renderables[a](this.renderer,e)}e.render();for(var a=0;a<this.hotspots.length;++a){this.hotspots[a].layout()}this.endRender(d,c)}},renderUpdated:function(d,c){if(!this.disposed&&this.renderer.supportsUpdate()){this.beginRender(d,c);var e=this.renderer.createTexturedQuadScene();for(var b in this.vrFaces){if(this.vrFaces[b].isUpdated()){this.vrFaces[b].render(e)}}e.render();for(var a=0;a<this.hotspots.length;++a){this.hotspots[a].layout()}this.endRender(d,c)}else{this.render(d,c)}},dragMode:bigshot.VRPanorama.DRAG_GRAB,setDragMode:function(a){this.dragMode=a},addEventListeners:function(){for(var a in this.allListeners){this.browser.registerListener(this.container,a,this.allListeners[a],false)}},removeEventListeners:function(){for(var a in this.allListeners){this.browser.unregisterListener(this.container,a,this.allListeners[a],false)}},dragMouseDown:function(a){this.dragStart={clientX:a.clientX,clientY:a.clientY};this.dragLast={clientX:a.clientX,clientY:a.clientY,dx:0,dy:0,dt:1000000,time:new Date().getTime()};this.dragDistance=0},dragMouseUp:function(i){if(this.dragStart==null||this.dragLast==null){this.dragStart=null;this.dragLast=null;return}this.dragStart=null;var l=this.dragLast.dx;var k=this.dragLast.dy;var d=Math.sqrt(l*l+k*k);var c=this.dragLast.dt;var b=new Date().getTime()-this.dragLast.time;this.dragLast=null;var j=c>0?(d/c):0;if(j>0.05&&b<250&&c>20&&this.parameters.fling){var f=this.state.fov/this.renderer.getViewportHeight();var g=new Date().getTime();var a=this.parameters.flingScale;l/=c;k/=c;this.smoothRotate(function(n){var m=new Date().getTime()-g;var e=Math.pow(2,-m*a);var o=(l*n*f)*e;return e>0.01?o:null},function(n){var m=new Date().getTime()-g;var e=Math.pow(2,-m*a);var o=(k*n*f)*e;return e>0.01?o:null},function(){return null});return true}else{this.smoothRotate();return false}},dragMouseMove:function(d){if(this.dragStart!=null&&this.currentGesture==null){if(this.dragMode==bigshot.VRPanorama.DRAG_GRAB){this.smoothRotate();var f=this.state.fov/this.renderer.getViewportHeight();var b=d.clientX-this.dragStart.clientX;var a=d.clientY-this.dragStart.clientY;this.dragDistance+=b+a;this.setYaw(this.getYaw()-b*f);this.setPitch(this.getPitch()-a*f);this.renderAsap();this.dragStart=d;var c=new Date().getTime()-this.dragLast.time;if(c>20){this.dragLast={dx:this.dragLast.clientX-d.clientX,dy:this.dragLast.clientY-d.clientY,dt:c,clientX:d.clientX,clientY:d.clientY,time:new Date().getTime()}}}else{var f=0.1*this.state.fov/this.renderer.getViewportHeight();var b=d.clientX-this.dragStart.clientX;var a=d.clientY-this.dragStart.clientY;this.dragDistance=b+a;this.smoothRotate(function(){return b*f},function(){return a*f})}}},onMouseDoubleClick:function(c,a,d){var b=this.createVREventData({type:"dblclick",clientX:c.clientX,clientY:c.clientY});this.fireEvent("dblclick",b);if(!b.defaultPrevented){this.smoothRotateToXY(a,d)}},mouseDoubleClick:function(a){var b=this.browser.getElementPosition(this.container);this.onMouseDoubleClick(a,a.clientX-b.x,a.clientY-b.y)},gestureStart:function(a){this.currentGesture={startFov:this.getFov(),scale:a.scale}},gestureEnd:function(a){this.currentGesture=null},gestureChange:function(a){if(this.currentGesture){var b=this.currentGesture.startFov/a.scale;this.setFov(b);this.renderAsap()}},setMaxTextureMagnification:function(a){this.maxTextureMagnification=a},getMaxTextureMagnification:function(){return this.maxTextureMagnification},getMinFovFromViewportAndImage:function(){var a=this.renderer.getViewportHeight()/2;var f=this.vrFaces[0].parameters.height;for(var c in this.vrFaces){f=Math.min(f,this.vrFaces[c].parameters.height)}var b=this.maxTextureMagnification*f/2;var d=a/b;var e=Math.atan(d)*180/Math.PI;return e*2},screenToRay:function(a,d){var c=this.screenToRayDelta(a,d);var b=this.renderer.transformToWorld(c);b=Matrix.RotationY(-this.transformOffsets.y*Math.PI/180).ensure4x4().xPoint3Dhom1(b);b=Matrix.RotationX(-this.transformOffsets.p*Math.PI/180).ensure4x4().xPoint3Dhom1(b);b=Matrix.RotationZ(-this.transformOffsets.r*Math.PI/180).ensure4x4().xPoint3Dhom1(b);return b},screenToRayDelta:function(j,i){var g=this.renderer.getViewportHeight()/2;var d=this.renderer.getViewportWidth()/2;var j=(j-d);var i=(i-g);var e=Math.tan((this.state.fov/2)*Math.PI/180);var f=e*this.renderer.getViewportWidth()/this.renderer.getViewportHeight();var c=j*f/d;var b=i*e/g;var a=-1;return{x:c,y:b,z:a}},smoothRotateToXY:function(a,c){var b=this.screenToPolar(a,c);this.smoothRotateTo(this.snapYaw(b.yaw),this.snapPitch(b.pitch),this.getFov(),this.state.fov/200)},ease:function(f,e,c,a){var b=c*40;if(!a){a=c/5}var d=c/1000;var g=f-e;if(g>b){g=-c}else{if(g<-b){g=c}else{if(Math.abs(g)<a){g=-g}else{if(Math.abs(g)<d){g=0}else{g=-(c*g)/(b)}}}}return g},resetIdle:function(){this.idleCounter=0},idleTick:function(){if(this.maxIdleCounter<0){return}++this.idleCounter;if(this.idleCounter==this.maxIdleCounter){this.autoRotate()}var a=this;setTimeout(function(){a.idleTick()},1000)},autoRotateWhenIdle:function(a){this.maxIdleCounter=a;this.idleCounter=0;if(a<0){return}else{if(this.maxIdleCounter>0){var b=this;setTimeout(function(){b.idleTick()},1000)}}},autoRotate:function(){var b=this;var d=this.state.fov/400;var c=d;var a=c;this.smoothRotate(function(){var e=b.getYaw()+a;if(b.parameters.minYaw<b.parameters.maxYaw){if(e>b.parameters.maxYaw||e<b.parameters.minYaw){a=-a}}else{if(e>b.parameters.minYaw){}else{if(e>b.parameters.maxYaw){a=-a}else{}}}return a},function(){return b.ease(b.getPitch(),0,c)},function(){return b.ease(b.getFov(),45,0.1)})},smoothRotateTo:function(e,d,a,c){var b=this;this.smoothRotate(function(){var g=b.circleDistance(e,b.getYaw());var f=-b.ease(0,g,c);return Math.abs(f)>0.01?f:null},function(){var f=b.ease(b.getPitch(),d,c);return Math.abs(f)>0.01?f:null},function(){var f=b.ease(b.getFov(),a,c);return Math.abs(f)>0.01?f:null})},smoothRotate:function(c,g,f){++this.smoothrotatePermit;var d=this.smoothrotatePermit;if(!g&&!c&&!f){return}var e=this;var b={dy:c,dp:g,df:f,t:new Date().getTime()};var a=function(){if(e.smoothrotatePermit==d){var i=new Date().getTime();var k=i-b.t;b.t=i;var j=false;if(b.dy){var l=b.dy(k);if(l!=null){j=true;e.setYaw(e.getYaw()+l)}else{b.dy=null}}if(b.dp){var l=b.dp(k);if(l!=null){j=true;e.setPitch(e.getPitch()+l)}else{b.dp=null}}if(b.df){var l=b.df(k);if(l!=null){j=true;e.setFov(e.getFov()+l)}else{b.df=null}}e.render();if(j){e.browser.requestAnimationFrame(a,e.renderer.getElement())}}};a()},mouseWheel:function(a){var b=0;if(!a){a=window.event}if(a.wheelDelta){b=a.wheelDelta/120;if(window.opera){b=-b}}else{if(a.detail){b=-a.detail}}if(b){this.mouseWheelHandler(b)}if(a.preventDefault){a.preventDefault()}a.returnValue=false},mouseWheelHandler:function(c){var a=this;var b=null;if(c>0){if(this.getFov()>this.parameters.minFov){b=this.getFov()*0.9}}if(c<0){if(this.getFov()<this.parameters.maxFov){b=this.getFov()/0.9}}if(b!=null){this.smoothRotate(null,null,function(){var d=(b-a.getFov())/1.5;return Math.abs(d)>0.01?d:null})}},fullScreen:function(a){if(this.fullScreenHandler){return}var d=document.createElement("div");d.style.position="absolute";d.style.fontSize="16pt";d.style.top="128px";d.style.width="100%";d.style.color="white";d.style.padding="16px";d.style.zIndex="9999";d.style.textAlign="center";d.style.opacity="0.75";d.innerHTML="<span style='border-radius: 16px; -moz-border-radius: 16px; padding: 16px; padding-left: 32px; padding-right: 32px; background:black'>Press Esc to exit full screen mode.</span>";var c=this;this.fullScreenHandler=new bigshot.FullScreen(this.container);this.fullScreenHandler.restoreSize=this.sizeContainer==null;this.fullScreenHandler.addOnResize(function(){c.onresize()});this.fullScreenHandler.addOnClose(function(){if(d.parentNode){try{div.removeChild(d)}catch(e){}}c.fullScreenHandler=null});if(a){this.fullScreenHandler.addOnClose(function(){a()})}this.removeEventListeners();this.fullScreenHandler.open();this.addEventListeners();var b=function(){c.render()};setTimeout(b,1000);setTimeout(b,2000);setTimeout(b,3000);if(this.fullScreenHandler.getRootElement()){this.fullScreenHandler.getRootElement().appendChild(d);setTimeout(function(){var f=0.75;var e=function(){f-=0.02;if(d.parentNode){if(f<=0){d.style.display="none";try{div.removeChild(d)}catch(g){}}else{d.style.opacity=f;setTimeout(e,20)}}};setTimeout(e,20)},3500)}return function(){c.removeEventListeners();c.fullScreenHandler.close();c.addEventListeners()}},onresize:function(){if(this.fullScreenHandler==null||!this.fullScreenHandler.isFullScreen){if(this.sizeContainer){var a=this.browser.getElementSize(this.sizeContainer);this.renderer.resize(a.w,a.h)}}else{this.container.style.width=window.innerWidth+"px";this.container.style.height=window.innerHeight+"px";var a=this.browser.getElementSize(this.container);this.renderer.resize(a.w,a.h)}this.renderer.onresize();this.renderAsap()},renderAsap:function(){if(!this.renderAsapPermitTaken&&!this.disposed){this.renderAsapPermitTaken=true;var a=this;this.browser.requestAnimationFrame(function(){a.renderAsapPermitTaken=false;a.render()},this.renderer.getElement())}},autoResizeContainer:function(a){this.sizeContainer=a}};bigshot.Object.extend(bigshot.VRPanorama,bigshot.EventDispatcher);bigshot.VRHotspot=function(a){this.panorama=a;this.clippingStrategy=bigshot.VRHotspot.CLIP_ADJUST(a)};bigshot.VRHotspot.CLIP_FRACTION=function(b,a){return function(f){var g={x0:Math.max(f.x,0),y0:Math.max(f.y,0),x1:Math.min(f.x+f.w,b.renderer.getViewportWidth()),y1:Math.min(f.y+f.h,b.renderer.getViewportHeight())};var e=f.w*f.h;var d=(g.x1-g.x0);var c=(g.y1-g.y0);if(d>0&&c>0){var i=d*c;return(i/e)>=a}else{return false}}};bigshot.VRHotspot.CLIP_CENTER=function(a){return function(b){var d={x:b.x+b.w/2,y:b.y+b.h/2};return d.x>=0&&d.x<a.renderer.getViewportWidth()&&d.y>=0&&d.y<a.renderer.getViewportHeight()}};bigshot.VRHotspot.CLIP_ADJUST=function(a){return function(b){if(b.x<0){b.w-=-b.x;b.x=0}if(b.y<0){b.h-=-b.y;b.y=0}if(b.x+b.w>a.renderer.getViewportWidth()){b.w=a.renderer.getViewportWidth()-b.x-1}if(b.y+b.h>a.renderer.getViewportHeight()){b.h=a.renderer.getViewportHeight()-b.y-1}return b.w>0&&b.h>0}};bigshot.VRHotspot.CLIP_ZOOM=function(a,c,b){return function(d){if(d.x>=0&&d.y>=0&&(d.x+c.w)<a.renderer.getViewportWidth()&&(d.y+c.h)<a.renderer.getViewportHeight()){d.w=c.w;d.h=c.h;return true}var f=0;if(d.x<0){f=Math.max(-d.x,f)}if(d.y<0){f=Math.max(-d.y,f)}if(d.x+c.w>a.renderer.getViewportWidth()){f=Math.max(d.x+c.w-a.renderer.getViewportWidth(),f)}if(d.y+c.h>a.renderer.getViewportHeight()){f=Math.max(d.y+c.h-a.renderer.getViewportHeight(),f)}f/=a.renderer.getViewportHeight();if(f>b){return false}var e=1/(1+f);d.w=c.w*e;d.h=c.w*e;if(d.x<0){d.x=0}if(d.y<0){d.y=0}if(d.x+d.w>a.renderer.getViewportWidth()){d.x=a.renderer.getViewportWidth()-d.w}if(d.y+d.h>a.renderer.getViewportHeight()){d.y=a.renderer.getViewportHeight()-d.h}return true}};bigshot.VRHotspot.CLIP_FADE=function(a,b){return function(c){var d=Math.min(c.x,c.y,a.renderer.getViewportWidth()-(c.x+c.w),a.renderer.getViewportHeight()-(c.y+c.h));if(d<=0){return false}else{if(d<=b){c.opacity=(d/b);return true}else{c.opacity=1;return true}}}};bigshot.VRHotspot.prototype={layout:function(){},rotate:function(d,c,b){var e=d*Math.PI/180;var a=Matrix.Rotation(e,$V([c.x,c.y,c.z])).ensure4x4();return a.xPoint3Dhom1(b)},toVector:function(c,b){var a={x:0,y:0,z:-1};a=this.rotate(-b,{x:1,y:0,z:0},a);a=this.rotate(-c,{x:0,y:1,z:0},a);return a},toScreen:function(b){var a=this.panorama.renderer.transformToScreen(b);return a},clip:function(a){return this.clippingStrategy(a)}};bigshot.VRPointHotspot=function(c,e,d,b,a,f){bigshot.VRHotspot.call(this,c);this.element=b;this.offsetX=a;this.offsetY=f;this.point=this.toVector(e,d)};bigshot.VRPointHotspot.prototype={layout:function(){var b=this.toScreen(this.point);var c=false;if(b!=null){var a=this.panorama.browser.getElementSize(this.element);b.w=a.w;b.h=a.h;b.x+=this.offsetX;b.y+=this.offsetY;if(this.clip(b)){this.element.style.top=(b.y)+"px";this.element.style.left=(b.x)+"px";this.element.style.width=(b.w)+"px";this.element.style.height=(b.h)+"px";if(b.opacity){this.element.style.opacity=b.opacity}this.element.style.visibility="inherit";c=true}}if(!c){this.element.style.visibility="hidden"}}};bigshot.Object.extend(bigshot.VRPointHotspot,bigshot.VRHotspot);bigshot.Object.validate("bigshot.VRPointHotspot",bigshot.VRHotspot);bigshot.VRRectangleHotspot=function(d,f,b,e,a,c){bigshot.VRHotspot.call(this,d);this.element=c;this.point0=this.toVector(f,b);this.point1=this.toVector(e,a)};bigshot.VRRectangleHotspot.prototype={layout:function(){var a=this.toScreen(this.point0);var d=this.toScreen(this.point1);var c=false;if(a!=null&&d!=null){var b={x:a.x,y:a.y,opacity:1,w:d.x-a.x,h:d.y-a.y};if(this.clip(b)){this.element.style.top=(b.y)+"px";this.element.style.left=(b.x)+"px";this.element.style.width=(b.w)+"px";this.element.style.height=(b.h)+"px";this.element.style.visibility="inherit";c=true}}if(!c){this.element.style.visibility="hidden"}}};bigshot.Object.extend(bigshot.VRRectangleHotspot,bigshot.VRHotspot);bigshot.Object.validate("bigshot.VRRectangleHotspot",bigshot.VRHotspot);bigshot.AdaptiveLODMonitorParameters=function(b){this.vrPanorama=null;this.targetFps=30;this.tolerance=0.3;this.rate=0.1;this.minMag=1.5;this.maxMag=16;this.hqRenderMag=1.5;this.hqRenderDelay=2000;this.hqRenderInterval=1000;if(b){for(var a in b){this[a]=b[a]}}this.merge=function(d,e){for(var c in d){if(e||!this[c]){this[c]=d[c]}}};return this};bigshot.AdaptiveLODMonitor=function(b){this.setParameters(b);this.currentAdaptiveMagnification=b.vrPanorama.getMaxTextureMagnification();this.frames=0;this.samples=0;this.renderTimeTotal=0;this.renderTimeLast=0;this.samplesLast=0;this.startTime=0;this.lastRender=0;this.hqRender=false;this.hqMode=false;this.hqRenderWaiting=false;this.enabled=true;var a=this;this.listenerFunction=function(e,d,c){a.listener(e,d,c)}};bigshot.AdaptiveLODMonitor.prototype={averageRenderTime:function(){if(this.samples>0){return this.renderTimeTotal/this.samples}else{return -1}},setParameters:function(a){this.parameters=a;this.targetTime=1000/this.parameters.targetFps;this.lowerTime=this.targetTime/(1+this.parameters.tolerance);this.upperTime=this.targetTime*(1+this.parameters.tolerance)},setEnabled:function(a){this.enabled=a},averageRenderTimeLast:function(){if(this.samples>0){return this.renderTimeLast/this.samplesLast}else{return -1}},getListener:function(){return this.listenerFunction},increaseDetail:function(){this.currentAdaptiveMagnification=Math.max(this.parameters.minMag,this.currentAdaptiveMagnification/(1+this.parameters.rate))},decreaseDetail:function(){this.currentAdaptiveMagnification=Math.min(this.parameters.maxMag,this.currentAdaptiveMagnification*(1+this.parameters.rate))},sample:function(){var b=new Date().getTime()-this.startTime;this.samples++;this.renderTimeTotal+=b;this.samplesLast++;this.renderTimeLast+=b;if(this.samplesLast>4){var a=this.renderTimeLast/this.samplesLast;if(a<this.lowerTime){this.increaseDetail()}else{if(a>this.upperTime){this.decreaseDetail()}}this.samplesLast=0;this.renderTimeLast=0}},hqRenderTick:function(){if(this.lastRender<new Date().getTime()-this.parameters.hqRenderDelay){this.hqRender=true;this.hqMode=true;if(this.enabled){this.parameters.vrPanorama.setMaxTextureMagnification(this.parameters.hqRenderMag);this.parameters.vrPanorama.render()}this.hqRender=false;this.hqRenderWaiting=false}else{var a=this;setTimeout(function(){a.hqRenderTick()},this.parameters.hqRenderInterval)}},listener:function(d,c,b){if(!this.enabled){return}if(this.hqRender){return}if(this.hqMode&&c==bigshot.VRPanorama.ONRENDER_TEXTURE_UPDATE){this.parameters.vrPanorama.setMaxTextureMagnification(this.parameters.minMag);return}else{this.hqMode=false}this.parameters.vrPanorama.setMaxTextureMagnification(this.currentAdaptiveMagnification);this.frames++;if((this.frames<20||this.frames%5==0)&&d==bigshot.VRPanorama.ONRENDER_BEGIN){this.startTime=new Date().getTime();this.lastRender=this.startTime;var a=this;setTimeout(function(){a.sample()},1);if(!this.hqRenderWaiting){this.hqRenderWaiting=true;setTimeout(function(){a.hqRenderTick()},this.parameters.hqRenderInterval)}}}}}; |
|
|
|
;(function(factory) { |
|
'use strict'; |
|
/* global window: false, define: false, module: false */ |
|
var root = typeof window === 'undefined' ? null : window; |
|
|
|
if (typeof define === 'function' && define.amd) { |
|
define(function(){ return factory(root); }); |
|
} else if (typeof module !== 'undefined') { |
|
module.exports = factory(root); |
|
} else { |
|
root.DOMPurify = factory(root); |
|
} |
|
}(function factory(window) { |
|
'use strict'; |
|
|
|
var DOMPurify = function(window) { |
|
return factory(window); |
|
}; |
|
|
|
/** |
|
* Version label, exposed for easier checks |
|
* if DOMPurify is up to date or not |
|
*/ |
|
DOMPurify.version = '0.8.6'; |
|
|
|
/** |
|
* Array of elements that DOMPurify removed during sanitation. |
|
* Empty if nothing was removed. |
|
*/ |
|
DOMPurify.removed = []; |
|
|
|
if (!window || !window.document || window.document.nodeType !== 9) { |
|
// not running in a browser, provide a factory function |
|
// so that you can pass your own Window |
|
DOMPurify.isSupported = false; |
|
return DOMPurify; |
|
} |
|
|
|
var document = window.document; |
|
var originalDocument = document; |
|
var DocumentFragment = window.DocumentFragment; |
|
var HTMLTemplateElement = window.HTMLTemplateElement; |
|
var Node = window.Node; |
|
var NodeFilter = window.NodeFilter; |
|
var NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap; |
|
var Text = window.Text; |
|
var Comment = window.Comment; |
|
|
|
// As per issue #47, the web-components registry is inherited by a |
|
// new document created via createHTMLDocument. As per the spec |
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) |
|
// a new empty registry is used when creating a template contents owner |
|
// document, so we use that as our parent document to ensure nothing |
|
// is inherited. |
|
if (typeof HTMLTemplateElement === 'function') { |
|
var template = document.createElement('template'); |
|
if (template.content && template.content.ownerDocument) { |
|
document = template.content.ownerDocument; |
|
} |
|
} |
|
var implementation = document.implementation; |
|
var createNodeIterator = document.createNodeIterator; |
|
var getElementsByTagName = document.getElementsByTagName; |
|
var createDocumentFragment = document.createDocumentFragment; |
|
var importNode = originalDocument.importNode; |
|
|
|
var hooks = {}; |
|
|
|
/** |
|
* Expose whether this browser supports running the full DOMPurify. |
|
*/ |
|
DOMPurify.isSupported = |
|
typeof implementation.createHTMLDocument !== 'undefined' && |
|
document.documentMode !== 9; |
|
|
|
/* Add properties to a lookup table */ |
|
var _addToSet = function(set, array) { |
|
var l = array.length; |
|
while (l--) { |
|
if (typeof array[l] === 'string') { |
|
array[l] = array[l].toLowerCase(); |
|
} |
|
set[array[l]] = true; |
|
} |
|
return set; |
|
}; |
|
|
|
/* Shallow clone an object */ |
|
var _cloneObj = function(object) { |
|
var newObject = {}; |
|
var property; |
|
for (property in object) { |
|
if (object.hasOwnProperty(property)) { |
|
newObject[property] = object[property]; |
|
} |
|
} |
|
return newObject; |
|
}; |
|
|
|
/** |
|
* We consider the elements and attributes below to be safe. Ideally |
|
* don't add any new ones but feel free to remove unwanted ones. |
|
*/ |
|
|
|
/* allowed element names */ |
|
var ALLOWED_TAGS = null; |
|
var DEFAULT_ALLOWED_TAGS = _addToSet({}, [ |
|
|
|
// HTML |
|
'a','abbr','acronym','address','area','article','aside','audio','b', |
|
'bdi','bdo','big','blink','blockquote','body','br','button','canvas', |
|
'caption','center','cite','code','col','colgroup','content','data', |
|
'datalist','dd','decorator','del','details','dfn','dir','div','dl','dt', |
|
'element','em','fieldset','figcaption','figure','font','footer','form', |
|
'h1','h2','h3','h4','h5','h6','head','header','hgroup','hr','html','i', |
|
'img','input','ins','kbd','label','legend','li','main','map','mark', |
|
'marquee','menu','menuitem','meter','nav','nobr','ol','optgroup', |
|
'option','output','p','pre','progress','q','rp','rt','ruby','s','samp', |
|
'section','select','shadow','small','source','spacer','span','strike', |
|
'strong','style','sub','summary','sup','table','tbody','td','template', |
|
'textarea','tfoot','th','thead','time','tr','track','tt','u','ul','var', |
|
'video','wbr', |
|
|
|
// SVG |
|
'svg','altglyph','altglyphdef','altglyphitem','animatecolor', |
|
'animatemotion','animatetransform','circle','clippath','defs','desc', |
|
'ellipse','filter','font','g','glyph','glyphref','hkern','image','line', |
|
'lineargradient','marker','mask','metadata','mpath','path','pattern', |
|
'polygon','polyline','radialgradient','rect','stop','switch','symbol', |
|
'text','textpath','title','tref','tspan','view','vkern', |
|
|
|
// SVG Filters |
|
'feBlend','feColorMatrix','feComponentTransfer','feComposite', |
|
'feConvolveMatrix','feDiffuseLighting','feDisplacementMap', |
|
'feFlood','feFuncA','feFuncB','feFuncG','feFuncR','feGaussianBlur', |
|
'feMerge','feMergeNode','feMorphology','feOffset', |
|
'feSpecularLighting','feTile','feTurbulence', |
|
|
|
//MathML |
|
'math','menclose','merror','mfenced','mfrac','mglyph','mi','mlabeledtr', |
|
'mmuliscripts','mn','mo','mover','mpadded','mphantom','mroot','mrow', |
|
'ms','mpspace','msqrt','mystyle','msub','msup','msubsup','mtable','mtd', |
|
'mtext','mtr','munder','munderover', |
|
|
|
//Text |
|
'#text' |
|
]); |
|
|
|
/* Allowed attribute names */ |
|
var ALLOWED_ATTR = null; |
|
var DEFAULT_ALLOWED_ATTR = _addToSet({}, [ |
|
|
|
// HTML |
|
'accept','action','align','alt','autocomplete','background','bgcolor', |
|
'border','cellpadding','cellspacing','checked','cite','class','clear','color', |
|
'cols','colspan','coords','datetime','default','dir','disabled', |
|
'download','enctype','face','for','headers','height','hidden','high','href', |
|
'hreflang','id','ismap','label','lang','list','loop', 'low','max', |
|
'maxlength','media','method','min','multiple','name','noshade','novalidate', |
|
'nowrap','open','optimum','pattern','placeholder','poster','preload','pubdate', |
|
'radiogroup','readonly','rel','required','rev','reversed','role','rows', |
|
'rowspan','spellcheck','scope','selected','shape','size','span', |
|
'srclang','start','src','step','style','summary','tabindex','title', |
|
'type','usemap','valign','value','width','xmlns', |
|
|
|
// SVG |
|
'accent-height','accumulate','additivive','alignment-baseline', |
|
'ascent','attributename','attributetype','azimuth','basefrequency', |
|
'baseline-shift','begin','bias','by','clip','clip-path','clip-rule', |
|
'color','color-interpolation','color-interpolation-filters','color-profile', |
|
'color-rendering','cx','cy','d','dx','dy','diffuseconstant','direction', |
|
'display','divisor','dur','edgemode','elevation','end','fill','fill-opacity', |
|
'fill-rule','filter','flood-color','flood-opacity','font-family','font-size', |
|
'font-size-adjust','font-stretch','font-style','font-variant','font-weight', |
|
'fx', 'fy','g1','g2','glyph-name','glyphref','gradientunits','gradienttransform', |
|
'image-rendering','in','in2','k','k1','k2','k3','k4','kerning','keypoints', |
|
'keysplines','keytimes','lengthadjust','letter-spacing','kernelmatrix', |
|
'kernelunitlength','lighting-color','local','marker-end','marker-mid', |
|
'marker-start','markerheight','markerunits','markerwidth','maskcontentunits', |
|
'maskunits','max','mask','mode','min','numoctaves','offset','operator', |
|
'opacity','order','orient','orientation','origin','overflow','paint-order', |
|
'path','pathlength','patterncontentunits','patterntransform','patternunits', |
|
'points','preservealpha','r','rx','ry','radius','refx','refy','repeatcount', |
|
'repeatdur','restart','result','rotate','scale','seed','shape-rendering', |
|
'specularconstant','specularexponent','spreadmethod','stddeviation','stitchtiles', |
|
'stop-color','stop-opacity','stroke-dasharray','stroke-dashoffset','stroke-linecap', |
|
'stroke-linejoin','stroke-miterlimit','stroke-opacity','stroke','stroke-width', |
|
'surfacescale','targetx','targety','transform','text-anchor','text-decoration', |
|
'text-rendering','textlength','u1','u2','unicode','values','viewbox', |
|
'visibility','vert-adv-y','vert-origin-x','vert-origin-y','word-spacing', |
|
'wrap','writing-mode','xchannelselector','ychannelselector','x','x1','x2', |
|
'y','y1','y2','z','zoomandpan', |
|
|
|
// MathML |
|
'accent','accentunder','bevelled','close','columnsalign','columnlines', |
|
'columnspan','denomalign','depth','display','displaystyle','fence', |
|
'frame','largeop','length','linethickness','lspace','lquote', |
|
'mathbackground','mathcolor','mathsize','mathvariant','maxsize', |
|
'minsize','movablelimits','notation','numalign','open','rowalign', |
|
'rowlines','rowspacing','rowspan','rspace','rquote','scriptlevel', |
|
'scriptminsize','scriptsizemultiplier','selection','separator', |
|
'separators','stretchy','subscriptshift','supscriptshift','symmetric', |
|
'voffset', |
|
|
|
// XML |
|
'xlink:href','xml:id','xlink:title','xml:space','xmlns:xlink' |
|
]); |
|
|
|
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ |
|
var FORBID_TAGS = null; |
|
|
|
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ |
|
var FORBID_ATTR = null; |
|
|
|
/* Decide if ARIA attributes are okay */ |
|
var ALLOW_ARIA_ATTR = true; |
|
|
|
/* Decide if custom data attributes are okay */ |
|
var ALLOW_DATA_ATTR = true; |
|
|
|
/* Decide if unknown protocols are okay */ |
|
var ALLOW_UNKNOWN_PROTOCOLS = false; |
|
|
|
/* Output should be safe for jQuery's $() factory? */ |
|
var SAFE_FOR_JQUERY = false; |
|
|
|
/* Output should be safe for common template engines. |
|
* This means, DOMPurify removes data attributes, mustaches and ERB |
|
*/ |
|
var SAFE_FOR_TEMPLATES = false; |
|
|
|
/* Specify template detection regex for SAFE_FOR_TEMPLATES mode */ |
|
var MUSTACHE_EXPR = /\{\{[\s\S]*|[\s\S]*\}\}/gm; |
|
var ERB_EXPR = /<%[\s\S]*|[\s\S]*%>/gm; |
|
|
|
/* Decide if document with <html>... should be returned */ |
|
var WHOLE_DOCUMENT = false; |
|
|
|
/* Decide if all elements (e.g. style, script) must be children of |
|
* document.body. By default, browsers might move them to document.head */ |
|
var FORCE_BODY = false; |
|
|
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html string. |
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead |
|
*/ |
|
var RETURN_DOM = false; |
|
|
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html string */ |
|
var RETURN_DOM_FRAGMENT = false; |
|
|
|
/* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM |
|
* `Node` is imported into the current `Document`. If this flag is not enabled the |
|
* `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by |
|
* DOMPurify. */ |
|
var RETURN_DOM_IMPORT = false; |
|
|
|
/* Output should be free from DOM clobbering attacks? */ |
|
var SANITIZE_DOM = true; |
|
|
|
/* Keep element content when removing element? */ |
|
var KEEP_CONTENT = true; |
|
|
|
/* Tags to ignore content of when KEEP_CONTENT is true */ |
|
var FORBID_CONTENTS = _addToSet({}, [ |
|
'audio', 'head', 'math', 'script', 'style', 'svg', 'video' |
|
]); |
|
|
|
/* Tags that are safe for data: URIs */ |
|
var DATA_URI_TAGS = _addToSet({}, [ |
|
'audio', 'video', 'img', 'source', 'image' |
|
]); |
|
|
|
/* Attributes safe for values like "javascript:" */ |
|
var URI_SAFE_ATTRIBUTES = _addToSet({}, [ |
|
'alt','class','for','id','label','name','pattern','placeholder', |
|
'summary','title','value','style','xmlns' |
|
]); |
|
|
|
/* Keep a reference to config to pass to hooks */ |
|
var CONFIG = null; |
|
|
|
/* Ideally, do not touch anything below this line */ |
|
/* ______________________________________________ */ |
|
|
|
var formElement = document.createElement('form'); |
|
|
|
/** |
|
* _parseConfig |
|
* |
|
* @param optional config literal |
|
*/ |
|
var _parseConfig = function(cfg) { |
|
/* Shield configuration object from tampering */ |
|
if (typeof cfg !== 'object') { |
|
cfg = {}; |
|
} |
|
|
|
/* Set configuration parameters */ |
|
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? |
|
_addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; |
|
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? |
|
_addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; |
|
FORBID_TAGS = 'FORBID_TAGS' in cfg ? |
|
_addToSet({}, cfg.FORBID_TAGS) : {}; |
|
FORBID_ATTR = 'FORBID_ATTR' in cfg ? |
|
_addToSet({}, cfg.FORBID_ATTR) : {}; |
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true |
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true |
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false |
|
SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false |
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false |
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false |
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false |
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false |
|
RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false |
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false |
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true |
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true |
|
|
|
if (SAFE_FOR_TEMPLATES) { |
|
ALLOW_DATA_ATTR = false; |
|
} |
|
|
|
if (RETURN_DOM_FRAGMENT) { |
|
RETURN_DOM = true; |
|
} |
|
|
|
/* Merge configuration parameters */ |
|
if (cfg.ADD_TAGS) { |
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { |
|
ALLOWED_TAGS = _cloneObj(ALLOWED_TAGS); |
|
} |
|
_addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); |
|
} |
|
if (cfg.ADD_ATTR) { |
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { |
|
ALLOWED_ATTR = _cloneObj(ALLOWED_ATTR); |
|
} |
|
_addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); |
|
} |
|
if (cfg.ADD_URI_SAFE_ATTR) { |
|
_addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); |
|
} |
|
|
|
/* Add #text in case KEEP_CONTENT is set to true */ |
|
if (KEEP_CONTENT) { ALLOWED_TAGS['#text'] = true; } |
|
|
|
// Prevent further manipulation of configuration. |
|
// Not available in IE8, Safari 5, etc. |
|
if (Object && 'freeze' in Object) { Object.freeze(cfg); } |
|
|
|
CONFIG = cfg; |
|
}; |
|
|
|
/** |
|
* _forceRemove |
|
* |
|
* @param a DOM node |
|
*/ |
|
var _forceRemove = function(node) { |
|
DOMPurify.removed.push({element: node}); |
|
try { |
|
node.parentNode.removeChild(node); |
|
} catch (e) { |
|
node.outerHTML = ''; |
|
} |
|
}; |
|
|
|
/** |
|
* _removeAttribute |
|
* |
|
* @param an Attribute name |
|
* @param a DOM node |
|
*/ |
|
var _removeAttribute = function(name, node) { |
|
DOMPurify.removed.push({ |
|
attribute: node.getAttributeNode(name), |
|
from: node |
|
}); |
|
node.removeAttribute(name); |
|
}; |
|
|
|
/** |
|
* _initDocument |
|
* |
|
* @param a string of dirty markup |
|
* @return a DOM, filled with the dirty markup |
|
*/ |
|
var _initDocument = function(dirty) { |
|
/* Create a HTML document */ |
|
var doc, body; |
|
|
|
if (FORCE_BODY) { |
|
dirty = '<remove></remove>' + dirty; |
|
} |
|
|
|
if (!doc || !doc.documentElement) { |
|
doc = implementation.createHTMLDocument(''); |
|
body = doc.body; |
|
body.parentNode.removeChild(body.parentNode.firstElementChild); |
|
body.outerHTML = dirty; |
|
} |
|
|
|
/* Work on whole document or just its body */ |
|
if (typeof doc.getElementsByTagName === 'function') { |
|
return doc.getElementsByTagName( |
|
WHOLE_DOCUMENT ? 'html' : 'body')[0]; |
|
} |
|
return getElementsByTagName.call(doc, |
|
WHOLE_DOCUMENT ? 'html' : 'body')[0]; |
|
}; |
|
|
|
/** |
|
* _createIterator |
|
* |
|
* @param document/fragment to create iterator for |
|
* @return iterator instance |
|
*/ |
|
var _createIterator = function(root) { |
|
return createNodeIterator.call(root.ownerDocument || root, |
|
root, |
|
NodeFilter.SHOW_ELEMENT |
|
| NodeFilter.SHOW_COMMENT |
|
| NodeFilter.SHOW_TEXT, |
|
function() { return NodeFilter.FILTER_ACCEPT; }, |
|
false |
|
); |
|
}; |
|
|
|
/** |
|
* _isClobbered |
|
* |
|
* @param element to check for clobbering attacks |
|
* @return true if clobbered, false if safe |
|
*/ |
|
var _isClobbered = function(elm) { |
|
if (elm instanceof Text || elm instanceof Comment) { |
|
return false; |
|
} |
|
if ( typeof elm.nodeName !== 'string' |
|
|| typeof elm.textContent !== 'string' |
|
|| typeof elm.removeChild !== 'function' |
|
|| !(elm.attributes instanceof NamedNodeMap) |
|
|| typeof elm.removeAttribute !== 'function' |
|
|| typeof elm.setAttribute !== 'function' |
|
) { |
|
return true; |
|
} |
|
return false; |
|
}; |
|
|
|
/** |
|
* _isNode |
|
* |
|
* @param object to check whether it's a DOM node |
|
* @return true is object is a DOM node |
|
*/ |
|
var _isNode = function(obj) { |
|
return ( |
|
typeof Node === "object" ? obj instanceof Node : obj |
|
&& typeof obj === "object" && typeof obj.nodeType === "number" |
|
&& typeof obj.nodeName==="string" |
|
); |
|
}; |
|
|
|
/** |
|
* _sanitizeElements |
|
* |
|
* @protect nodeName |
|
* @protect textContent |
|
* @protect removeChild |
|
* |
|
* @param node to check for permission to exist |
|
* @return true if node was killed, false if left alive |
|
*/ |
|
var _sanitizeElements = function(currentNode) { |
|
var tagName, content; |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('beforeSanitizeElements', currentNode, null); |
|
|
|
/* Check if element is clobbered or can clobber */ |
|
if (_isClobbered(currentNode)) { |
|
_forceRemove(currentNode); |
|
return true; |
|
} |
|
|
|
/* Now let's check the element's type and name */ |
|
tagName = currentNode.nodeName.toLowerCase(); |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('uponSanitizeElement', currentNode, { |
|
tagName: tagName, |
|
allowedTags: ALLOWED_TAGS |
|
}); |
|
|
|
/* Remove element if anything forbids its presence */ |
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { |
|
/* Keep content except for black-listed elements */ |
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] |
|
&& typeof currentNode.insertAdjacentHTML === 'function') { |
|
try { |
|
currentNode.insertAdjacentHTML('AfterEnd', currentNode.innerHTML); |
|
} catch (e) {} |
|
} |
|
_forceRemove(currentNode); |
|
return true; |
|
} |
|
|
|
/* Convert markup to cover jQuery behavior */ |
|
if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && |
|
(!currentNode.content || !currentNode.content.firstElementChild) && |
|
/</g.test(currentNode.textContent)) { |
|
DOMPurify.removed.push({element: currentNode.cloneNode()}); |
|
currentNode.innerHTML = currentNode.textContent.replace(/</g, '<'); |
|
} |
|
|
|
/* Sanitize element content to be template-safe */ |
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { |
|
/* Get the element's text content */ |
|
content = currentNode.textContent; |
|
content = content.replace(MUSTACHE_EXPR, ' '); |
|
content = content.replace(ERB_EXPR, ' '); |
|
if (currentNode.textContent !== content) { |
|
DOMPurify.removed.push({element: currentNode.cloneNode()}); |
|
currentNode.textContent = content; |
|
} |
|
} |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('afterSanitizeElements', currentNode, null); |
|
|
|
return false; |
|
}; |
|
|
|
var DATA_ATTR = /^data-[\-\w.\u00B7-\uFFFF]/; |
|
var ARIA_ATTR = /^aria-[\-\w]+$/; |
|
var IS_ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; |
|
var IS_SCRIPT_OR_DATA = /^(?:\w+script|data):/i; |
|
/* This needs to be extensive thanks to Webkit/Blink's behavior */ |
|
var ATTR_WHITESPACE = /[\x00-\x20\xA0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; |
|
|
|
/** |
|
* _sanitizeAttributes |
|
* |
|
* @protect attributes |
|
* @protect nodeName |
|
* @protect removeAttribute |
|
* @protect setAttribute |
|
* |
|
* @param node to sanitize |
|
* @return void |
|
*/ |
|
var _sanitizeAttributes = function(currentNode) { |
|
var attr, name, value, lcName, idAttr, attributes, hookEvent, l; |
|
/* Execute a hook if present */ |
|
_executeHook('beforeSanitizeAttributes', currentNode, null); |
|
|
|
attributes = currentNode.attributes; |
|
|
|
/* Check if we have attributes; if not we might have a text node */ |
|
if (!attributes) { return; } |
|
|
|
hookEvent = { |
|
attrName: '', |
|
attrValue: '', |
|
keepAttr: true, |
|
allowedAttributes: ALLOWED_ATTR |
|
}; |
|
l = attributes.length; |
|
|
|
/* Go backwards over all attributes; safely remove bad ones */ |
|
while (l--) { |
|
attr = attributes[l]; |
|
name = attr.name; |
|
value = attr.value.trim(); |
|
lcName = name.toLowerCase(); |
|
|
|
/* Execute a hook if present */ |
|
hookEvent.attrName = lcName; |
|
hookEvent.attrValue = value; |
|
hookEvent.keepAttr = true; |
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent ); |
|
value = hookEvent.attrValue; |
|
|
|
/* Remove attribute */ |
|
// Safari (iOS + Mac), last tested v8.0.5, crashes if you try to |
|
// remove a "name" attribute from an <img> tag that has an "id" |
|
// attribute at the time. |
|
if (lcName === 'name' && |
|
currentNode.nodeName === 'IMG' && attributes.id) { |
|
idAttr = attributes.id; |
|
attributes = Array.prototype.slice.apply(attributes); |
|
_removeAttribute('id', currentNode); |
|
_removeAttribute(name, currentNode); |
|
if (attributes.indexOf(idAttr) > l) { |
|
currentNode.setAttribute('id', idAttr.value); |
|
} |
|
} else if ( |
|
// This works around a bug in Safari, where input[type=file] |
|
// cannot be dynamically set after type has been removed |
|
currentNode.nodeName === 'INPUT' && lcName === 'type' && |
|
value === 'file' && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) { |
|
continue; |
|
|
|
} else { |
|
// This avoids a crash in Safari v9.0 with double-ids. |
|
// The trick is to first set the id to be empty and then to |
|
// remove the attriubute |
|
if (name === 'id') { |
|
currentNode.setAttribute(name, ''); |
|
} |
|
_removeAttribute(name, currentNode); |
|
} |
|
|
|
/* Did the hooks approve of the attribute? */ |
|
if (!hookEvent.keepAttr) { |
|
continue; |
|
} |
|
|
|
/* Make sure attribute cannot clobber */ |
|
if (SANITIZE_DOM && |
|
(lcName === 'id' || lcName === 'name') && |
|
(value in window || value in document || value in formElement)) { |
|
continue; |
|
} |
|
|
|
/* Sanitize attribute content to be template-safe */ |
|
if (SAFE_FOR_TEMPLATES) { |
|
value = value.replace(MUSTACHE_EXPR, ' '); |
|
value = value.replace(ERB_EXPR, ' '); |
|
} |
|
|
|
/* Allow valid data-* attributes: At least one character after "-" |
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) |
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) |
|
We don't need to check the value; it's always URI safe. */ |
|
if (ALLOW_DATA_ATTR && DATA_ATTR.test(lcName)) { |
|
// This attribute is safe |
|
} |
|
else if (ALLOW_ARIA_ATTR && ARIA_ATTR.test(lcName)) { |
|
// This attribute is safe |
|
} |
|
/* Otherwise, check the name is permitted */ |
|
else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { |
|
continue; |
|
} |
|
/* Check value is safe. First, is attr inert? If so, is safe */ |
|
else if (URI_SAFE_ATTRIBUTES[lcName]) { |
|
// This attribute is safe |
|
} |
|
/* Check no script, data or unknown possibly unsafe URI |
|
unless we know URI values are safe for that attribute */ |
|
else if (IS_ALLOWED_URI.test(value.replace(ATTR_WHITESPACE,''))) { |
|
// This attribute is safe |
|
} |
|
/* Keep image data URIs alive if src/xlink:href is allowed */ |
|
else if ( |
|
(lcName === 'src' || lcName === 'xlink:href') && |
|
value.indexOf('data:') === 0 && |
|
DATA_URI_TAGS[currentNode.nodeName.toLowerCase()]) { |
|
// This attribute is safe |
|
} |
|
/* Allow unknown protocols: This provides support for links that |
|
are handled by protocol handlers which may be unknown ahead of |
|
time, e.g. fb:, spotify: */ |
|
else if ( |
|
ALLOW_UNKNOWN_PROTOCOLS && |
|
!IS_SCRIPT_OR_DATA.test(value.replace(ATTR_WHITESPACE,''))) { |
|
// This attribute is safe |
|
} |
|
/* Check for binary attributes */ |
|
else if (!value) { |
|
// binary attributes are safe at this point |
|
} |
|
/* Anything else, presume unsafe, do not add it back */ |
|
else { |
|
continue; |
|
} |
|
|
|
/* Handle invalid data-* attribute set by try-catching it */ |
|
try { |
|
currentNode.setAttribute(name, value); |
|
DOMPurify.removed.pop(); |
|
} catch (e) {} |
|
} |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('afterSanitizeAttributes', currentNode, null); |
|
}; |
|
|
|
/** |
|
* _sanitizeShadowDOM |
|
* |
|
* @param fragment to iterate over recursively |
|
* @return void |
|
*/ |
|
var _sanitizeShadowDOM = function(fragment) { |
|
var shadowNode; |
|
var shadowIterator = _createIterator(fragment); |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('beforeSanitizeShadowDOM', fragment, null); |
|
|
|
while ( (shadowNode = shadowIterator.nextNode()) ) { |
|
/* Execute a hook if present */ |
|
_executeHook('uponSanitizeShadowNode', shadowNode, null); |
|
|
|
/* Sanitize tags and elements */ |
|
if (_sanitizeElements(shadowNode)) { |
|
continue; |
|
} |
|
|
|
/* Deep shadow DOM detected */ |
|
if (shadowNode.content instanceof DocumentFragment) { |
|
_sanitizeShadowDOM(shadowNode.content); |
|
} |
|
|
|
/* Check attributes, sanitize if necessary */ |
|
_sanitizeAttributes(shadowNode); |
|
} |
|
|
|
/* Execute a hook if present */ |
|
_executeHook('afterSanitizeShadowDOM', fragment, null); |
|
}; |
|
|
|
/** |
|
* _executeHook |
|
* Execute user configurable hooks |
|
* |
|
* @param {String} entryPoint Name of the hook's entry point |
|
* @param {Node} currentNode |
|
*/ |
|
var _executeHook = function(entryPoint, currentNode, data) { |
|
if (!hooks[entryPoint]) { return; } |
|
|
|
hooks[entryPoint].forEach(function(hook) { |
|
hook.call(DOMPurify, currentNode, data, CONFIG); |
|
}); |
|
}; |
|
|
|
/** |
|
* sanitize |
|
* Public method providing core sanitation functionality |
|
* |
|
* @param {String|Node} dirty string or DOM node |
|
* @param {Object} configuration object |
|
*/ |
|
DOMPurify.sanitize = function(dirty, cfg) { |
|
var body, importedNode, currentNode, oldNode, nodeIterator, returnNode; |
|
/* Make sure we have a string to sanitize. |
|
DO NOT return early, as this will return the wrong type if |
|
the user has requested a DOM object rather than a string */ |
|
if (!dirty) { |
|
dirty = '<!-->'; |
|
} |
|
|
|
/* Stringify, in case dirty is an object */ |
|
if (typeof dirty !== 'string' && !_isNode(dirty)) { |
|
if (typeof dirty.toString !== 'function') { |
|
throw new TypeError('toString is not a function'); |
|
} else { |
|
dirty = dirty.toString(); |
|
} |
|
} |
|
|
|
/* Check we can run. Otherwise fall back or ignore */ |
|
if (!DOMPurify.isSupported) { |
|
if (typeof window.toStaticHTML === 'object' |
|
|| typeof window.toStaticHTML === 'function') { |
|
if (typeof dirty === 'string') { |
|
return window.toStaticHTML(dirty); |
|
} else if (_isNode(dirty)) { |
|
return window.toStaticHTML(dirty.outerHTML); |
|
} |
|
} |
|
return dirty; |
|
} |
|
|
|
/* Assign config vars */ |
|
_parseConfig(cfg); |
|
|
|
/* Clean up removed elements */ |
|
DOMPurify.removed = []; |
|
|
|
if (dirty instanceof Node) { |
|
/* If dirty is a DOM element, append to an empty document to avoid |
|
elements being stripped by the parser */ |
|
body = _initDocument('<!-->'); |
|
importedNode = body.ownerDocument.importNode(dirty, true); |
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { |
|
/* Node is already a body, use as is */ |
|
body = importedNode; |
|
} else { |
|
body.appendChild( importedNode ); |
|
} |
|
} else { |
|
/* Exit directly if we have nothing to do */ |
|
if (!RETURN_DOM && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) { |
|
return dirty; |
|
} |
|
|
|
/* Initialize the document to work on */ |
|
body = _initDocument(dirty); |
|
|
|
/* Check we have a DOM node from the data */ |
|
if (!body) { |
|
return RETURN_DOM ? null : ''; |
|
} |
|
} |
|
|
|
/* Remove first element node (ours) if FORCE_BODY is set */ |
|
if (FORCE_BODY) { |
|
_forceRemove(body.firstChild); |
|
} |
|
|
|
/* Get node iterator */ |
|
nodeIterator = _createIterator(body); |
|
|
|
/* Now start iterating over the created document */ |
|
while ( (currentNode = nodeIterator.nextNode()) ) { |
|
|
|
/* Fix IE's strange behavior with manipulated textNodes #89 */ |
|
if (currentNode.nodeType === 3 && currentNode === oldNode) { |
|
continue; |
|
} |
|
|
|
/* Sanitize tags and elements */ |
|
if (_sanitizeElements(currentNode)) { |
|
continue; |
|
} |
|
|
|
/* Shadow DOM detected, sanitize it */ |
|
if (currentNode.content instanceof DocumentFragment) { |
|
_sanitizeShadowDOM(currentNode.content); |
|
} |
|
|
|
/* Check attributes, sanitize if necessary */ |
|
_sanitizeAttributes(currentNode); |
|
|
|
oldNode = currentNode; |
|
} |
|
|
|
/* Return sanitized string or DOM */ |
|
if (RETURN_DOM) { |
|
|
|
if (RETURN_DOM_FRAGMENT) { |
|
returnNode = createDocumentFragment.call(body.ownerDocument); |
|
|
|
while (body.firstChild) { |
|
returnNode.appendChild(body.firstChild); |
|
} |
|
} else { |
|
returnNode = body; |
|
} |
|
|
|
if (RETURN_DOM_IMPORT) { |
|
/* adoptNode() is not used because internal state is not reset |
|
(e.g. the past names map of a HTMLFormElement), this is safe |
|
in theory but we would rather not risk another attack vector. |
|
The state that is cloned by importNode() is explicitly defined |
|
by the specs. */ |
|
returnNode = importNode.call(originalDocument, returnNode, true); |
|
} |
|
|
|
return returnNode; |
|
} |
|
|
|
return WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; |
|
}; |
|
|
|
/** |
|
* addHook |
|
* Public method to add DOMPurify hooks |
|
* |
|
* @param {String} entryPoint |
|
* @param {Function} hookFunction |
|
*/ |
|
DOMPurify.addHook = function(entryPoint, hookFunction) { |
|
if (typeof hookFunction !== 'function') { return; } |
|
hooks[entryPoint] = hooks[entryPoint] || []; |
|
hooks[entryPoint].push(hookFunction); |
|
}; |
|
|
|
/** |
|
* removeHook |
|
* Public method to remove a DOMPurify hook at a given entryPoint |
|
* (pops it from the stack of hooks if more are present) |
|
* |
|
* @param {String} entryPoint |
|
* @return void |
|
*/ |
|
DOMPurify.removeHook = function(entryPoint) { |
|
if (hooks[entryPoint]) { |
|
hooks[entryPoint].pop(); |
|
} |
|
}; |
|
|
|
/** |
|
* removeHooks |
|
* Public method to remove all DOMPurify hooks at a given entryPoint |
|
* |
|
* @param {String} entryPoint |
|
* @return void |
|
*/ |
|
DOMPurify.removeHooks = function(entryPoint) { |
|
if (hooks[entryPoint]) { |
|
hooks[entryPoint] = []; |
|
} |
|
}; |
|
|
|
/** |
|
* removeAllHooks |
|
* Public method to remove all DOMPurify hooks |
|
* |
|
* @return void |
|
*/ |
|
DOMPurify.removeAllHooks = function() { |
|
hooks = {}; |
|
}; |
|
|
|
return DOMPurify; |
|
})); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global DOMPurify, oc_requesttoken, Gallery */ |
|
|
|
// The Utility class can also be loaded by the Files app |
|
window.Gallery = window.Gallery || {}; |
|
|
|
(function ($, OC, t, oc_requesttoken, Gallery) { |
|
"use strict"; |
|
/** |
|
* Contains utility methods |
|
* |
|
* @fixme OC.generateUrl, OC.buildQueryString, OC.Notification are private APIs |
|
* |
|
* @constructor |
|
*/ |
|
var Utility = function () { |
|
}; |
|
|
|
Utility.prototype = { |
|
/** |
|
* Detects if the browser is a recent or an old version of Internet Explorer |
|
* |
|
* @returns {string|boolean} |
|
*/ |
|
getIeVersion: function () { |
|
// Blocking IE8 |
|
if ($('html').is('.ie8')) { |
|
return 'unsupportedIe'; |
|
} else if (navigator.userAgent.indexOf("MSIE") > 0) { |
|
return 'unsupportedIe'; |
|
} else if (!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { |
|
return 'modernIe'; |
|
} else if (navigator.userAgent.indexOf("Edge/") > 0) { |
|
return 'edge'; |
|
} |
|
|
|
return false; |
|
}, |
|
|
|
/** |
|
* Shows a notification to IE users, letting them know that they should use another browser |
|
* in order to get the best experience |
|
* |
|
* @param {string} version |
|
*/ |
|
showIeWarning: function (version) { |
|
var line1 = t('gallery', 'This application may not work properly on your browser.'); |
|
var line2 = t('gallery', |
|
'For an improved experience, please install one of the following alternatives'); |
|
var timeout = 15; |
|
if (version === 'unsupportedIe') { |
|
line1 = t('gallery', 'Your browser is not supported!'); |
|
line2 = t('gallery', 'please install one of the following alternatives'); |
|
timeout = 60; |
|
} |
|
|
|
var recommendedBrowsers = '</br>' + |
|
'<a href="http://www.getfirefox.com"><strong>Mozilla Firefox</strong></a> or ' + |
|
'<a href="https://www.google.com/chrome/"><strong>Google Chrome</strong></a>' + |
|
'</br>'; |
|
|
|
var text = '<strong>' + line1 + '</strong></br>' + line2 + recommendedBrowsers; |
|
this.showHtmlNotification(text, timeout); |
|
}, |
|
|
|
/** |
|
* Shows a notification at the top of the screen |
|
* |
|
* @param {string} text |
|
* @param {int} timeout |
|
*/ |
|
showHtmlNotification: function (text, timeout) { |
|
var options = { |
|
timeout: timeout, |
|
isHTML: true |
|
}; |
|
OC.Notification.showTemporary(text, options); |
|
}, |
|
|
|
/** |
|
* Returns the token allowing access to files shared via link |
|
* |
|
* @returns {string} |
|
*/ |
|
getPublicToken: function () { |
|
var element = $('#gallery'); |
|
var token; |
|
|
|
if (element.data('token')) { |
|
token = element.data('token'); |
|
} |
|
|
|
if (element.data('requesttoken')) { |
|
/* jshint camelcase: false */ |
|
oc_requesttoken = element.data('requesttoken'); |
|
} |
|
|
|
return token; |
|
}, |
|
|
|
/** |
|
* Returns the host we can use for WebDAV |
|
* |
|
* On public galleries, we need to provide the token as authorization |
|
* |
|
* @returns {string} |
|
*/ |
|
getWebdavHost: function () { |
|
var host = OC.getHost(); |
|
if (Gallery.token) { |
|
host = Gallery.token + '@' + host; |
|
} |
|
|
|
return host; |
|
}, |
|
|
|
/** |
|
* Returns the WebDAV endpoint we can use for files operations |
|
* |
|
* @returns {string} |
|
*/ |
|
getWebdavRoot: function () { |
|
var root = OC.linkToRemoteBase('webdav'); |
|
if (Gallery.token) { |
|
root = root.replace('remote.php', 'public.php'); |
|
} |
|
|
|
return root; |
|
}, |
|
|
|
/** |
|
* Builds the URL which will retrieve a large preview of the file |
|
* |
|
* @fixme we cannot get rid of oc_requesttoken parameter as it's missing from the headers |
|
* |
|
* @param {number} fileId |
|
* @param {number} etag |
|
* |
|
* @return {string} |
|
*/ |
|
getPreviewUrl: function (fileId, etag) { |
|
var width = Math.ceil(screen.width * window.devicePixelRatio); |
|
var height = Math.ceil(screen.height * window.devicePixelRatio); |
|
|
|
/* Find value of longest edge. */ |
|
var longEdge = Math.max(width, height); |
|
|
|
/* Find the next larger image size. */ |
|
if (longEdge % 100 !== 0) { |
|
longEdge = ( longEdge + 100 ) - ( longEdge % 100 ); |
|
} |
|
|
|
/* jshint camelcase: false */ |
|
var params = { |
|
width: longEdge, |
|
height: longEdge, |
|
}; |
|
return this.buildGalleryUrl('preview', '/' + fileId, params); |
|
}, |
|
|
|
/** |
|
* Builds a URL pointing to one of the app's controllers |
|
* |
|
* @param {string} endPoint |
|
* @param {undefined|string} path |
|
* @param params |
|
* |
|
* @returns {string} |
|
*/ |
|
buildGalleryUrl: function (endPoint, path, params) { |
|
if (path === undefined) { |
|
path = ''; |
|
} |
|
var extension = ''; |
|
if (Gallery.token) { |
|
params.token = Gallery.token; |
|
extension = '.public'; |
|
} |
|
var query = OC.buildQueryString(params); |
|
return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension + path, |
|
null) + |
|
'?' + |
|
query; |
|
}, |
|
|
|
/** |
|
* Builds a URL pointing to one of the files' controllers |
|
* |
|
* @param {string} path |
|
* @param {string} files |
|
* |
|
* @returns {string} |
|
*/ |
|
buildFilesUrl: function (path, files) { |
|
var subUrl = ''; |
|
var params = { |
|
path: path, |
|
files: files |
|
}; |
|
|
|
if (Gallery.token) { |
|
params.token = Gallery.token; |
|
subUrl = 's/{token}/download?dir={path}&files={files}'; |
|
} else { |
|
subUrl = 'apps/files/ajax/download.php?dir={path}&files={files}'; |
|
} |
|
|
|
return OC.generateUrl(subUrl, params); |
|
}, |
|
|
|
/** |
|
* Sorts arrays based on name or date |
|
* |
|
* @param {string} sortType |
|
* @param {string} sortOrder |
|
* |
|
* @returns {Function} |
|
*/ |
|
sortBy: function (sortType, sortOrder) { |
|
if (sortType === 'name') { |
|
if (sortOrder === 'asc') { |
|
//sortByNameAsc |
|
return function (a, b) { |
|
return OC.Util.naturalSortCompare(a.path, b.path); |
|
}; |
|
} |
|
//sortByNameDes |
|
return function (a, b) { |
|
return -OC.Util.naturalSortCompare(a.path, b.path); |
|
}; |
|
} |
|
if (sortType === 'date') { |
|
if (sortOrder === 'asc') { |
|
//sortByDateAsc |
|
return function (a, b) { |
|
return b.mTime - a.mTime; |
|
}; |
|
} |
|
//sortByDateDes |
|
return function (a, b) { |
|
return a.mTime - b.mTime; |
|
}; |
|
} |
|
}, |
|
|
|
/** |
|
* Adds hooks to DOMPurify |
|
*/ |
|
addDomPurifyHooks: function () { |
|
// allowed URI schemes |
|
var whitelist = ['http', 'https']; |
|
|
|
// build fitting regex |
|
var regex = new RegExp('^(' + whitelist.join('|') + '):', 'gim'); |
|
|
|
DOMPurify.addHook('afterSanitizeAttributes', function (node) { |
|
// This hook enforces URI scheme whitelist |
|
// @link |
|
// https://github.com/cure53/DOMPurify/blob/master/demos/hooks-scheme-whitelist.html |
|
|
|
// build an anchor to map URLs to |
|
var anchor = document.createElement('a'); |
|
|
|
// check all href attributes for validity |
|
if (node.hasAttribute('href')) { |
|
anchor.href = node.getAttribute('href'); |
|
if (anchor.protocol && !anchor.protocol.match(regex)) { |
|
node.removeAttribute('href'); |
|
} |
|
} |
|
// check all action attributes for validity |
|
if (node.hasAttribute('action')) { |
|
anchor.href = node.getAttribute('action'); |
|
if (anchor.protocol && !anchor.protocol.match(regex)) { |
|
node.removeAttribute('action'); |
|
} |
|
} |
|
// check all xlink:href attributes for validity |
|
if (node.hasAttribute('xlink:href')) { |
|
anchor.href = node.getAttribute('xlink:href'); |
|
if (anchor.protocol && !anchor.protocol.match(regex)) { |
|
node.removeAttribute('xlink:href'); |
|
} |
|
} |
|
|
|
// This hook restores the proper, standard namespace in SVG files |
|
var encodedXmlns, decodedXmlns; |
|
|
|
// Restores namespaces which were put in the DOCTYPE by Illustrator |
|
if (node.hasAttribute('xmlns') && node.getAttribute('xmlns') === '&ns_svg;') { |
|
encodedXmlns = node.getAttribute('xmlns'); |
|
decodedXmlns = encodedXmlns.replace('&ns_svg;', 'http://www.w3.org/2000/svg'); |
|
node.setAttribute('xmlns', decodedXmlns); |
|
} |
|
if (node.hasAttribute('xmlns:xlink') && |
|
node.getAttribute('xmlns:xlink') === '&ns_xlink;') { |
|
encodedXmlns = node.getAttribute('xmlns:xlink'); |
|
decodedXmlns = |
|
encodedXmlns.replace('&ns_xlink;', 'http://www.w3.org/1999/xlink'); |
|
node.setAttribute('xmlns:xlink', decodedXmlns); |
|
} |
|
}); |
|
} |
|
}; |
|
|
|
Gallery.Utility = Utility; |
|
})(jQuery, OC, t, oc_requesttoken, Gallery); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global oc_requesttoken, FileList, Gallery, SlideShow */ |
|
(function ($, OC, OCA, oc_requesttoken) { |
|
"use strict"; |
|
var galleryFileAction = { |
|
features: [], |
|
mediaTypes: {}, |
|
scrollContainer: null, |
|
slideShow: null, |
|
|
|
/** |
|
* Builds a URL pointing to one of the app's controllers |
|
* |
|
* @param {string} endPoint |
|
* @param {undefined|string} path |
|
* @param {Object} params |
|
* |
|
* @returns {string} |
|
*/ |
|
buildGalleryUrl: function (endPoint, path, params) { |
|
var extension = ''; |
|
var tokenElement = $('#sharingToken'); |
|
var token = (tokenElement.val()) ? tokenElement.val() : false; |
|
if (token) { |
|
params.token = token; |
|
extension = '.public'; |
|
} |
|
var query = OC.buildQueryString(params); |
|
return OC.generateUrl('apps/gallery/' + endPoint + extension + path, null) + '?' + |
|
query; |
|
}, |
|
|
|
/** |
|
* Registers a file action for each media type |
|
* |
|
* @param {Array} mediaTypes |
|
*/ |
|
register: function (mediaTypes) { |
|
if (OCA.Viewer) { |
|
return; |
|
} |
|
//console.log("enabledPreviewProviders: ", mediaTypes); |
|
if (mediaTypes) { |
|
// Remove SVG if the user is using an insecure browser (IE8-9) |
|
if (window.galleryFileAction.features.indexOf('native_svg') > -1 && !window.btoa) { |
|
mediaTypes.splice(mediaTypes.indexOf('image/svg+xml'), 1); |
|
} |
|
galleryFileAction.mediaTypes = mediaTypes; |
|
} |
|
var i, mediaTypesLength = mediaTypes.length; |
|
// We only want to create slideshows for supported media types |
|
for (i = 0; i < mediaTypesLength; i++) { |
|
// Each click handler gets the same function and images array and |
|
// is responsible to load the slideshow |
|
OCA.Files.fileActions.register(mediaTypes[i], 'View', OC.PERMISSION_READ, '', |
|
galleryFileAction.onView); |
|
OCA.Files.fileActions.setDefault(mediaTypes[i], 'View'); |
|
} |
|
}, |
|
|
|
/** |
|
* Prepares the features array |
|
* |
|
* This is duplicated from a method found in galleryconfig. It's done that way in order to |
|
* avoid having to load the whole utility class in the Files app |
|
* |
|
* @param configFeatures |
|
* @returns {Array} |
|
*/ |
|
buildFeaturesList: function (configFeatures) { |
|
var features = []; |
|
var i, configFeaturesLength = configFeatures.length; |
|
if (configFeaturesLength) { |
|
for (i = 0; i < configFeaturesLength; i++) { |
|
features.push(configFeatures[i]); |
|
} |
|
} |
|
|
|
window.galleryFileAction.features = features; |
|
}, |
|
|
|
/** |
|
* Builds an array containing all the images we can show in the slideshow |
|
* |
|
* @param {string} filename |
|
* @param {Object} context |
|
*/ |
|
onView: function (filename, context) { |
|
var imageUrl, downloadUrl; |
|
var fileList = context.fileList; |
|
var files = fileList.files; |
|
var start = 0; |
|
var images = []; |
|
var dir = context.dir + '/'; |
|
var width = Math.ceil(screen.width * window.devicePixelRatio); |
|
var height = Math.ceil(screen.height * window.devicePixelRatio); |
|
|
|
/* Find value of longest edge. */ |
|
var longEdge = Math.max(width, height); |
|
|
|
/* Find the next larger image size. */ |
|
if (longEdge % 100 !== 0) { |
|
longEdge = ( longEdge + 100 ) - ( longEdge % 100 ); |
|
} |
|
|
|
for (var i = 0; i < files.length; i++) { |
|
var file = files[i]; |
|
// We only add images to the slideshow if we think we'll be able |
|
// to generate previews for this media type |
|
if (galleryFileAction.mediaTypes.indexOf(file.mimetype) > -1) { |
|
/* jshint camelcase: false */ |
|
var params = { |
|
width: longEdge, |
|
height: longEdge, |
|
c: file.etag, |
|
requesttoken: oc_requesttoken |
|
}; |
|
imageUrl = galleryFileAction.buildGalleryUrl('preview', '/' + file.id, params); |
|
params = { |
|
c: file.etag, |
|
requesttoken: oc_requesttoken |
|
}; |
|
downloadUrl = |
|
galleryFileAction.buildGalleryUrl('files', '/download/' + file.id, params); |
|
|
|
images.push({ |
|
name: file.name, |
|
path: dir + file.name, |
|
fileId: file.id, |
|
mimeType: file.mimetype, |
|
permissions: file.permissions, |
|
url: imageUrl, |
|
downloadUrl: downloadUrl |
|
}); |
|
} |
|
} |
|
for (i = 0; i < images.length; i++) { |
|
//console.log("Images in the slideshow : ", images[i]); |
|
if (images[i].name === filename) { |
|
start = i; |
|
} |
|
} |
|
|
|
if ($.isEmptyObject(galleryFileAction.slideShow)) { |
|
galleryFileAction.slideShow = new SlideShow(); |
|
$.when(galleryFileAction.slideShow.init( |
|
false, |
|
null, |
|
window.galleryFileAction.features |
|
)).then(function () { |
|
// Don't show the download button on the "Files" slideshow |
|
galleryFileAction._startSlideshow(images, start); |
|
}); |
|
} else { |
|
galleryFileAction._startSlideshow(images, start); |
|
} |
|
}, |
|
|
|
/** |
|
* Launches the slideshow |
|
* |
|
* @param {{name:string, url: string, path: string, fallBack: string}[]} images |
|
* @param {number} start |
|
* @private |
|
*/ |
|
_startSlideshow: function (images, start) { |
|
galleryFileAction.slideShow.setImages(images, false); |
|
|
|
var scrollTop = galleryFileAction.scrollContainer.scrollTop(); |
|
// This is only called when the slideshow is stopped |
|
galleryFileAction.slideShow.onStop = function () { |
|
FileList.$fileList.one('updated', function () { |
|
galleryFileAction.scrollContainer.scrollTop(scrollTop); |
|
}); |
|
}; |
|
|
|
// Only modern browsers can manipulate history |
|
if (history && history.replaceState) { |
|
// This stores the fileslist in the history state |
|
var stateData = { |
|
dir: FileList.getCurrentDirectory() |
|
}; |
|
history.replaceState(stateData, document.title, window.location); |
|
|
|
// This creates a new entry in history for the slideshow. It will |
|
// be updated as the user navigates from picture to picture |
|
history.pushState(null, '', '#loading'); |
|
} |
|
|
|
galleryFileAction.slideShow.show(start); |
|
} |
|
}; |
|
|
|
window.galleryFileAction = galleryFileAction; |
|
})(jQuery, OC, OCA, oc_requesttoken); |
|
|
|
$(document).ready(function () { |
|
"use strict"; |
|
// Deactivates fileaction on public preview page |
|
if ($('#imgframe').length > 0) { |
|
return true; |
|
} |
|
|
|
if ($('html').is('.ie8')) { |
|
return true; //deactivate in IE8 |
|
} |
|
|
|
window.galleryFileAction.scrollContainer = $('#app-content'); |
|
if ($('#isPublic').val()) { |
|
window.galleryFileAction.scrollContainer = $(window); |
|
} |
|
|
|
var utility = new Gallery.Utility(); |
|
utility.addDomPurifyHooks(); |
|
|
|
// Retrieve the config as well as the list of supported media types. |
|
// The list of media files is retrieved when the user clicks on a row |
|
var url = window.galleryFileAction.buildGalleryUrl('config', '', {extramediatypes: 1}); |
|
$.getJSON(url).then(function (config) { |
|
window.galleryFileAction.buildFeaturesList(config.features); |
|
window.galleryFileAction.register(config.mediatypes); |
|
}); |
|
|
|
// create public share links as from the files app |
|
if (!Gallery.appName) { |
|
Gallery.appName = 'files'; |
|
} |
|
}); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global Gallery, Thumbnails, DOMPurify */ |
|
(function ($, OC, OCA, t) { |
|
"use strict"; |
|
/** |
|
* Slideshow featuring zooming |
|
* |
|
* @constructor |
|
*/ |
|
var SlideShow = function () { |
|
}; |
|
|
|
SlideShow.prototype = { |
|
slideshowTemplate: null, |
|
container: null, |
|
zoomablePreviewContainer: null, |
|
controls: null, |
|
imageCache: {}, |
|
/** {Image} */ |
|
currentImage: null, |
|
errorLoadingImage: false, |
|
onStop: null, |
|
zoomablePreview: null, |
|
active: false, |
|
backgroundToggle: false, |
|
// We need 6 hexas for comparison reasons |
|
darkBackgroundColour: '#000000', |
|
lightBackgroundColour: '#ffffff', |
|
|
|
/** |
|
* Initialises the slideshow |
|
* |
|
* @param {boolean} autoPlay |
|
* @param {number} interval |
|
* @param {Array} features |
|
*/ |
|
init: function (autoPlay, interval, features) { |
|
if (features.indexOf('background_colour_toggle') > -1) { |
|
this.backgroundToggle = true; |
|
} |
|
|
|
return $.when(this._getSlideshowTemplate()).then(function ($tmpl) { |
|
// Move the slideshow outside the content so we can hide the content |
|
$('body').append($tmpl); |
|
this.container = $('#slideshow'); |
|
this.zoomablePreviewContainer = this.container.find('.bigshotContainer'); |
|
this.zoomablePreview = new SlideShow.ZoomablePreview(this.container); |
|
this.controls = |
|
new SlideShow.Controls( |
|
this, |
|
this.container, |
|
this.zoomablePreview, |
|
interval, |
|
features, |
|
this.restoreContent.bind(this)); |
|
this.controls.init(); |
|
|
|
this._initControlsAutoFader(); |
|
|
|
// Only modern browsers can manipulate history |
|
if (history && history.pushState) { |
|
// Stop the slideshow when backing out. |
|
$(window).bind('popstate.slideshow', function () { |
|
if (this.active === true) { |
|
this.active = false; |
|
this.controls.stop(); |
|
} |
|
}.bind(this)); |
|
} |
|
}.bind(this)).fail(function () { |
|
OC.Notification.show(t('gallery', 'Error loading slideshow template')); |
|
}); |
|
}, |
|
|
|
/** |
|
* Refreshes the slideshow's data |
|
* |
|
* @param {{name:string, url: string, path: string, fallBack: string}[]} images |
|
* @param {boolean} autoPlay |
|
*/ |
|
setImages: function (images, autoPlay) { |
|
this._hideImage(); |
|
this.images = images; |
|
this.controls.update(images, autoPlay); |
|
}, |
|
|
|
/** |
|
* Hides the content behind the slideshow |
|
* |
|
* This should be called when the slideshow is shown. |
|
* |
|
* It hides the content (and, in the public share page, also the footer) |
|
* to ensure that the body size is just the slideshow size and thus no |
|
* scroll bars are shown. |
|
*/ |
|
hideContent: function () { |
|
this._savedScrollPosition = $(window).scrollTop(); |
|
|
|
$('#content').hide(); |
|
$('footer').hide(); |
|
}, |
|
|
|
/** |
|
* Shows again the content behind the slideshow |
|
* |
|
* This should be called when the slideshow is hidden. |
|
* |
|
* It restores the content hidden when calling "hideContent", including |
|
* the vertical scrolling position. |
|
*/ |
|
restoreContent: function () { |
|
$('#content').show(); |
|
$('footer').show(); |
|
|
|
if (this._savedScrollPosition) { |
|
$(window).scrollTop(this._savedScrollPosition); |
|
} |
|
}, |
|
|
|
/** |
|
* Launches the slideshow |
|
* |
|
* @param {number} index |
|
* |
|
* @returns {*} |
|
*/ |
|
show: function (index) { |
|
this.hideErrorNotification(); |
|
this.active = true; |
|
this.container.show(); |
|
this.hideContent(); |
|
this.container.css('background-position', 'center'); |
|
this._hideImage(); |
|
this.container.find('.icon-loading-dark').show(); |
|
var currentImageId = index; |
|
return this.loadImage(this.images[index]).then(function (img) { |
|
this.container.css('background-position', '-10000px 0'); |
|
|
|
// check if we moved along while we were loading |
|
if (currentImageId === index) { |
|
var image = this.images[index]; |
|
var transparent = this._isTransparent(image.mimeType); |
|
this.controls.showActionButtons(transparent, Gallery.token, image.permissions); |
|
this.errorLoadingImage = false; |
|
this.currentImage = img; |
|
img.setAttribute('alt', image.name); |
|
$(img).css('position', 'absolute'); |
|
$(img).css('background-color', image.backgroundColour); |
|
if (transparent && this.backgroundToggle === true) { |
|
var $border = 30 / window.devicePixelRatio; |
|
$(img).css('outline', $border + 'px solid ' + image.backgroundColour); |
|
} |
|
|
|
this.zoomablePreview.startBigshot(img, this.currentImage, image.mimeType); |
|
|
|
this._setUrl(image.path); |
|
this.controls.show(currentImageId); |
|
this.container.find('.icon-loading-dark').hide(); |
|
} |
|
}.bind(this), function () { |
|
// Don't do anything if the user has moved along while we were loading as it would |
|
// mess up the index |
|
if (currentImageId === index) { |
|
this.errorLoadingImage = true; |
|
this.showErrorNotification(null); |
|
this._setUrl(this.images[index].path); |
|
this.images.splice(index, 1); |
|
this.controls.updateControls(this.images, this.errorLoadingImage); |
|
} |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Loads the image to show in the slideshow and preloads the next one |
|
* |
|
* @param {Object} preview |
|
* |
|
* @returns {*} |
|
*/ |
|
loadImage: function (preview) { |
|
var url = preview.url; |
|
var mimeType = preview.mimeType; |
|
|
|
if (!this.imageCache[url]) { |
|
this.imageCache[url] = new $.Deferred(); |
|
var image = new Image(); |
|
|
|
image.onload = function () { |
|
preview.backgroundColour = this._getBackgroundColour(image, mimeType); |
|
if (this.imageCache[url]) { |
|
this.imageCache[url].resolve(image); |
|
} |
|
}.bind(this); |
|
image.onerror = function () { |
|
if (this.imageCache[url]) { |
|
this.imageCache[url].reject(url); |
|
} |
|
}.bind(this); |
|
if (mimeType === 'image/svg+xml') { |
|
image.src = this._getSVG(url); |
|
} else { |
|
image.src = url; |
|
} |
|
} |
|
return this.imageCache[url]; |
|
}, |
|
|
|
/** |
|
* Shows a new image in the slideshow and preloads the next in the list |
|
* |
|
* @param {number} current |
|
* @param {Object} next |
|
*/ |
|
next: function (current, next) { |
|
this.show(current).then(function () { |
|
// Preloads the next image in the list |
|
this.loadImage(next); |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Determines which colour to use for the background |
|
* |
|
* @param {*} image |
|
* @param {string} mimeType |
|
* |
|
* @returns {string} |
|
* @private |
|
*/ |
|
_getBackgroundColour: function (image, mimeType) { |
|
var backgroundColour = this.darkBackgroundColour; |
|
if (this._isTransparent(mimeType) && this._isMainlyDark(image)) { |
|
backgroundColour = this.lightBackgroundColour; |
|
} |
|
return backgroundColour; |
|
}, |
|
|
|
/** |
|
* Calculates the luminance of an image to determine if an image is mainly dark |
|
* |
|
* @param {*} image |
|
* |
|
* @returns {boolean} |
|
* @private |
|
*/ |
|
_isMainlyDark: function (image) { |
|
var isMainlyDark = false; |
|
var numberOfSamples = 1000; // Seems to be the sweet spot |
|
// The name has to be 'canvas' |
|
var lumiCanvas = document.createElement('canvas'); |
|
|
|
var imgArea = image.width * image.height; |
|
var canArea = numberOfSamples; |
|
var factor = Math.sqrt(canArea / imgArea); |
|
|
|
var scaledWidth = factor * image.width; |
|
var scaledHeight = factor * image.height; |
|
lumiCanvas.width = scaledWidth; |
|
lumiCanvas.height = scaledHeight; |
|
var lumiCtx = lumiCanvas.getContext('2d'); |
|
lumiCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight); |
|
var imgData = lumiCtx.getImageData(0, 0, lumiCanvas.width, lumiCanvas.height); |
|
var pix = imgData.data; // pix.length will be approximately 4*numberOfSamples (for RGBA) |
|
var pixelArraySize = pix.length; |
|
var totalLuminance = 0; |
|
var sampleNumber = 1; |
|
var averageLuminance; |
|
var totalAlpha = 0; |
|
var alphaLevel; |
|
var red = 0; |
|
var green = 0; |
|
var blue = 0; |
|
var alpha = 0; |
|
var lum = 0; |
|
var alphaThreshold = 0.1; |
|
|
|
var sampleCounter = 0; |
|
var itemsPerPixel = 4; // red, green, blue, alpha |
|
// i += 4 because 4 colours for every pixel |
|
for (var i = 0, n = pixelArraySize; i < n; i += itemsPerPixel) { |
|
sampleCounter++; |
|
alpha = pix[i + 3] / 255; |
|
totalAlpha += alpha; |
|
if (Math.ceil(alpha * 100) / 100 > alphaThreshold) { |
|
red = pix[i]; |
|
green = pix[i + 1]; |
|
blue = pix[i + 2]; |
|
// Luminance formula from |
|
// http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color |
|
lum = (red + red + green + green + green + blue) / 6; |
|
//lum = (red * 0.299 + green * 0.587 + blue * 0.114 ); |
|
totalLuminance += lum * alpha; |
|
sampleNumber++; |
|
} |
|
} |
|
|
|
// Deletes the canvas |
|
lumiCanvas = null; |
|
|
|
// Calculate the optimum background colour for this image |
|
averageLuminance = Math.ceil((totalLuminance / sampleNumber) * 100) / 100; |
|
alphaLevel = Math.ceil((totalAlpha / numberOfSamples) * 100); |
|
|
|
if (averageLuminance < 60 && alphaLevel < 90) { |
|
isMainlyDark = true; |
|
} |
|
|
|
return isMainlyDark; |
|
}, |
|
|
|
/** |
|
* Stops the slideshow |
|
*/ |
|
stop: function () { |
|
this.active = false; |
|
this.images = null; |
|
this._hideImage(); |
|
if (this.onStop) { |
|
this.onStop(); |
|
} |
|
}, |
|
|
|
/** |
|
* Sends the current image as a download |
|
* |
|
* @param {string} downloadUrl |
|
* |
|
* @returns {boolean} |
|
*/ |
|
getImageDownload: function (downloadUrl) { |
|
OC.redirect(downloadUrl); |
|
return false; |
|
}, |
|
|
|
/** |
|
* Changes the colour of the background of the image |
|
*/ |
|
toggleBackground: function () { |
|
var toHex = function (x) { |
|
return ("0" + parseInt(x).toString(16)).slice(-2); |
|
}; |
|
var container = this.zoomablePreviewContainer.children('img'); |
|
var rgb = container.css('background-color').match(/\d+/g); |
|
var hex = "#" + toHex(rgb[0]) + toHex(rgb[1]) + toHex(rgb[2]); |
|
var $border = 30 / window.devicePixelRatio; |
|
var newBackgroundColor; |
|
|
|
// Grey #363636 |
|
if (hex === this.darkBackgroundColour) { |
|
newBackgroundColor = this.lightBackgroundColour; |
|
} else { |
|
newBackgroundColor = this.darkBackgroundColour; |
|
} |
|
|
|
container.css('background-color', newBackgroundColor); |
|
if (this.backgroundToggle === true) { |
|
container.css('outline', $border + 'px solid ' + newBackgroundColor); |
|
} |
|
}, |
|
|
|
/** |
|
* Shows an error notification |
|
* |
|
* @param {string} message |
|
*/ |
|
showErrorNotification: function (message) { |
|
if ($.isEmptyObject(message)) { |
|
message = t('gallery', |
|
'<strong>Error!</strong> Could not generate a preview of this file.<br>' + |
|
'Please go to the next slide while we remove this image from the slideshow'); |
|
} |
|
this.container.find('.notification').html(message); |
|
this.container.find('.notification').show(); |
|
this.controls.hideButton('.changeBackground'); |
|
}, |
|
|
|
/** |
|
* Hides the error notification |
|
*/ |
|
hideErrorNotification: function () { |
|
this.container.find('.notification').hide(); |
|
this.container.find('.notification').html(''); |
|
}, |
|
|
|
/** |
|
* Removes a specific button from the interface |
|
* |
|
* @param button |
|
*/ |
|
removeButton: function (button) { |
|
this.controls.removeButton(button); |
|
}, |
|
|
|
/** |
|
* Deletes an image from the slideshow |
|
* |
|
* @param {object} image |
|
* @param {number} currentIndex |
|
*/ |
|
deleteImage: function (image, currentIndex) { |
|
// These are Gallery specific commands to be replaced |
|
// which should sit somewhere else |
|
if (!window.galleryFileAction) { |
|
delete Gallery.imageMap[image.path]; |
|
delete Thumbnails.map[image.file]; |
|
Gallery.albumMap[Gallery.currentAlbum].images.splice(currentIndex, 1); |
|
Gallery.view.init(Gallery.currentAlbum); |
|
} |
|
}, |
|
|
|
/** |
|
* Automatically fades the controls after 3 seconds |
|
* |
|
* @private |
|
*/ |
|
_initControlsAutoFader: function () { |
|
var inactiveCallback = function () { |
|
this.container.addClass('inactive'); |
|
}.bind(this); |
|
var inactiveTimeout = setTimeout(inactiveCallback, 3000); |
|
|
|
this.container.on('mousemove touchstart', function () { |
|
this.container.removeClass('inactive'); |
|
clearTimeout(inactiveTimeout); |
|
inactiveTimeout = setTimeout(inactiveCallback, 3000); |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Simplest way to detect if image is transparent. |
|
* |
|
* That's very inaccurate since it doesn't include images which support transparency |
|
* |
|
* @param mimeType |
|
* @returns {boolean} |
|
* @private |
|
*/ |
|
_isTransparent: function (mimeType) { |
|
return !(mimeType === 'image/jpeg' |
|
|| mimeType === 'image/x-dcraw' |
|
|| mimeType === 'application/font-sfnt' |
|
|| mimeType === 'application/x-font' |
|
); |
|
}, |
|
|
|
/** |
|
* Changes the browser Url, based on the current image |
|
* |
|
* @param {string} path |
|
* @private |
|
*/ |
|
_setUrl: function (path) { |
|
if (history && history.replaceState) { |
|
history.replaceState('', '', '#' + encodeURI(path)); |
|
} |
|
}, |
|
|
|
/** |
|
* Hides the current image (before loading the next) |
|
* |
|
* @private |
|
*/ |
|
_hideImage: function () { |
|
this.zoomablePreviewContainer.empty(); |
|
this.controls.hideActionButtons(); |
|
}, |
|
|
|
/** |
|
* Retrieves an SVG |
|
* |
|
* An SVG can't be simply attached to a src attribute like a bitmap image |
|
* |
|
* @param {string} source |
|
* |
|
* @returns {*} |
|
* @private |
|
*/ |
|
_getSVG: function (source) { |
|
var svgPreview = null; |
|
// DOMPurify only works with IE10+ and we load SVGs in the IMG tag |
|
if (window.btoa && |
|
document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", |
|
"1.1")) { |
|
var xmlHttp = new XMLHttpRequest(); |
|
xmlHttp.open("GET", source, false); |
|
xmlHttp.send(null); |
|
if (xmlHttp.status === 200) { |
|
var pureSvg = DOMPurify.sanitize(xmlHttp.responseText, {ADD_TAGS: ['filter']}); |
|
// Remove XML comment garbage left in the purified data |
|
var badTag = pureSvg.indexOf(']>'); |
|
var fixedPureSvg = pureSvg.substring(badTag < 0 ? 0 : 5, pureSvg.length); |
|
svgPreview = "data:image/svg+xml;base64," + window.btoa(fixedPureSvg); |
|
} |
|
} |
|
|
|
return svgPreview; |
|
}, |
|
|
|
/** |
|
* Retrieves the slideshow's template |
|
* |
|
* @returns {*} |
|
* @private |
|
*/ |
|
_getSlideshowTemplate: function () { |
|
var defer = $.Deferred(); |
|
if (!this.$slideshowTemplate) { |
|
var self = this; |
|
var url = OC.generateUrl('apps/gallery/slideshow', null); |
|
$.get(url, function (tmpl) { |
|
var template = $(tmpl); |
|
var tmplButton; |
|
var buttonsArray = [ |
|
{ |
|
el: '.next', |
|
trans: t('gallery', 'Next') |
|
}, |
|
{ |
|
el: '.play', |
|
trans: t('gallery', 'Play'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.pause', |
|
trans: t('gallery', 'Pause'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.previous', |
|
trans: t('gallery', 'Previous') |
|
}, |
|
{ |
|
el: '.exit', |
|
trans: t('gallery', 'Close'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.downloadImage', |
|
trans: t('gallery', 'Download'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.changeBackground', |
|
trans: t('gallery', 'Toggle background'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.deleteImage', |
|
trans: t('gallery', 'Delete'), |
|
toolTip: true |
|
}, |
|
{ |
|
el: '.shareImage', |
|
trans: t('gallery', 'Share'), |
|
toolTip: true |
|
} |
|
]; |
|
for (var i = 0; i < buttonsArray.length; i++) { |
|
var button = buttonsArray[i]; |
|
|
|
tmplButton = template.find(button.el); |
|
tmplButton.val(button.trans); |
|
if (button.toolTip) { |
|
tmplButton.attr("title", button.trans); |
|
} |
|
} |
|
self.$slideshowTemplate = template; |
|
defer.resolve(self.$slideshowTemplate); |
|
}) |
|
.fail(function () { |
|
defer.reject(); |
|
}); |
|
} else { |
|
defer.resolve(this.$slideshowTemplate); |
|
} |
|
return defer.promise(); |
|
} |
|
}; |
|
|
|
window.SlideShow = SlideShow; |
|
})(jQuery, OC, OCA, t); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global OC, SlideShow */ |
|
(function ($, SlideShow) { |
|
"use strict"; |
|
/** |
|
* Button and key controls for the slideshow |
|
* |
|
* @param {Object} slideshow |
|
* @param {*} container |
|
* @param {Object} zoomablePreview |
|
* @param {number} interval |
|
* @param {Array} features |
|
* @param {Function} restoreContentCallback |
|
* @constructor |
|
*/ |
|
var Controls = function (slideshow, container, zoomablePreview, interval, features, restoreContentCallback) { |
|
this.slideshow = slideshow; |
|
this.container = container; |
|
this.zoomablePreview = zoomablePreview; |
|
this.progressBar = container.find('.progress'); |
|
this.interval = interval || 5000; |
|
if (features.indexOf('background_colour_toggle') > -1) { |
|
this.backgroundToggle = true; |
|
} |
|
this.restoreContentCallback = restoreContentCallback; |
|
}; |
|
|
|
Controls.prototype = { |
|
current: 0, |
|
errorLoadingImage: false, |
|
playTimeout: 0, |
|
playing: false, |
|
active: false, |
|
backgroundToggle: false, |
|
|
|
/** |
|
* Initialises the controls |
|
*/ |
|
init: function () { |
|
var makeCallBack = function (handler) { |
|
return function (evt) { |
|
if (!this.active) { |
|
return; |
|
} |
|
evt.stopPropagation(); |
|
evt.preventDefault(); |
|
handler.call(this); |
|
}.bind(this); |
|
}.bind(this); |
|
|
|
this._buttonSetup(makeCallBack); |
|
this._specialButtonSetup(makeCallBack); |
|
this._keyCodeSetup(makeCallBack); |
|
}, |
|
|
|
/** |
|
* Updates the controls |
|
* |
|
* @param {{name:string, url: string, path: string, fallBack: string}[]} images |
|
* @param {boolean} autoPlay |
|
*/ |
|
update: function (images, autoPlay) { |
|
this.images = images; |
|
this.active = true; |
|
this.showButton('.play'); |
|
this.hideButton('.pause, .progress'); |
|
this.playing = false; |
|
|
|
// Hide prev/next and play buttons when we only have one pic |
|
this.container.find('.next, .previous, .play').toggle(this.images.length > 1); |
|
|
|
// Hide the action buttons until we have something to show |
|
this.hideActionButtons(); |
|
|
|
if (autoPlay) { |
|
this._playPauseToggle(); |
|
} |
|
}, |
|
|
|
/** |
|
* Initialises local variables when the show starts |
|
* |
|
* @param {number} currentImageId |
|
*/ |
|
show: function (currentImageId) { |
|
var currentImage = this.images[currentImageId]; |
|
this.current = currentImageId; |
|
this.errorLoadingImage = false; |
|
if (this.playing) { |
|
this._setTimeout(); |
|
} |
|
this._setName(currentImage.name); |
|
}, |
|
|
|
/** |
|
* Stops and hides the slideshow |
|
*/ |
|
stop: function () { |
|
this._setName(''); |
|
this.playing = false; |
|
this.slideshow.stop(); |
|
this.zoomablePreview.stop(); |
|
|
|
this._clearTimeout(); |
|
this.restoreContentCallback(); |
|
this.container.hide(); |
|
this.active = false; |
|
}, |
|
|
|
/** |
|
* Updates the private variables in case of problems loading an image |
|
* |
|
* @param {Array} images |
|
* @param {boolean} errorLoadingImage |
|
*/ |
|
updateControls: function (images, errorLoadingImage) { |
|
this.images = images; |
|
this.errorLoadingImage = errorLoadingImage; |
|
}, |
|
|
|
/** |
|
* Shows the action buttons |
|
* |
|
* @param {boolean} transparent |
|
* @param {boolean} isPublic |
|
* @param {number} permissions |
|
*/ |
|
showActionButtons: function (transparent, isPublic, permissions) { |
|
if (transparent) { |
|
this._showBackgroundToggle(); |
|
} |
|
var hideDownload = $('#hideDownload').val(); |
|
if (hideDownload !== 'true') { |
|
this.showButton('.downloadImage'); |
|
} |
|
var canDelete = ((permissions & OC.PERMISSION_DELETE) !== 0); |
|
var canShare = ((permissions & OC.PERMISSION_SHARE) !== 0); |
|
if (!isPublic && canDelete) { |
|
this.showButton('.deleteImage'); |
|
} |
|
if (!isPublic && canShare) { |
|
this.showButton('.shareImage'); |
|
} |
|
}, |
|
|
|
/** |
|
* Hides the action buttons |
|
*/ |
|
hideActionButtons: function () { |
|
this.hideButton('.changeBackground'); |
|
this.hideButton('.downloadImage'); |
|
this.hideButton('.deleteImage'); |
|
this.hideButton('.shareImage'); |
|
}, |
|
|
|
/** |
|
* Shows a button which has been hidden |
|
*/ |
|
showButton: function (button) { |
|
this.container.find(button).removeClass('hidden'); |
|
}, |
|
|
|
/** |
|
* Hides a button |
|
* |
|
* @param button |
|
*/ |
|
hideButton: function (button) { |
|
this.container.find(button).addClass('hidden'); |
|
}, |
|
|
|
/** |
|
* Removes a button |
|
* |
|
* @param button |
|
*/ |
|
removeButton: function (button) { |
|
this.container.find(button).remove(); |
|
}, |
|
|
|
/** |
|
* Sets up the button based navigation |
|
* |
|
* @param {Function} makeCallBack |
|
* @private |
|
*/ |
|
_buttonSetup: function (makeCallBack) { |
|
this.container.children('.next').click(makeCallBack(this._next)); |
|
this.container.children('.previous').click(makeCallBack(this._previous)); |
|
this.container.children('.exit').click(makeCallBack(this._exit)); |
|
this.container.children('.pause, .play').click(makeCallBack(this._playPauseToggle)); |
|
this.progressBar.click(makeCallBack(this._playPauseToggle)); |
|
this.container.children('.previous, .next, .slideshow-menu, .name').on( |
|
'mousewheel DOMMouseScroll mousemove', function (evn) { |
|
this.container.children('.bigshotContainer')[0].dispatchEvent( |
|
new WheelEvent(evn.originalEvent.type, evn.originalEvent)); |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Sets up additional buttons |
|
* |
|
* @param {Function} makeCallBack |
|
* @private |
|
*/ |
|
_specialButtonSetup: function (makeCallBack) { |
|
this.container.find('.downloadImage').click(makeCallBack(this._getImageDownload)); |
|
this.container.find('.deleteImage').click(makeCallBack(this._deleteImage)); |
|
this.container.find('.shareImage').click(makeCallBack(this.share)); |
|
this.container.find('.slideshow-menu').width = 52; |
|
if (this.backgroundToggle) { |
|
this.container.find('.changeBackground').click( |
|
makeCallBack(this._toggleBackground)); |
|
this.container.find('.slideshow-menu').width += 52; |
|
} else { |
|
this.hideButton('.changeBackground'); |
|
} |
|
}, |
|
|
|
/** |
|
* Populates the share dialog with the needed information |
|
*/ |
|
share: function () { |
|
var image = this.images[this.current]; |
|
if (!Gallery.Share.droppedDown) { |
|
|
|
$('.slideshow-menu a.share').data('path', image.path) |
|
.data('link', true) |
|
.data('item-source', image.file) |
|
.data('possible-permissions', image.permissions) |
|
.click(); |
|
} |
|
}, |
|
|
|
/** |
|
* Shows the background colour switcher, if activated in the configuration |
|
*/ |
|
_showBackgroundToggle: function () { |
|
if (this.backgroundToggle) { |
|
this.showButton('.changeBackground'); |
|
} |
|
}, |
|
|
|
/** |
|
* Sets up the key based controls |
|
* |
|
* @param {Function} makeCallBack |
|
* @private |
|
*/ |
|
_keyCodeSetup: function (makeCallBack) { |
|
$(document).keyup(function (evt) { |
|
if (evt.target.tagName.toLowerCase() === 'input') { |
|
return; |
|
} |
|
|
|
var escKey = 27; |
|
var leftKey = 37; |
|
var rightKey = 39; |
|
var spaceKey = 32; |
|
var fKey = 70; |
|
var zoomOutKeys = [48, 96, 79, 40]; // zeros, o or down key |
|
var zoomInKeys = [57, 105, 73, 38]; // 9, i or up key |
|
if (evt.keyCode === escKey) { |
|
makeCallBack(this._exit)(evt); |
|
} else if (evt.keyCode === leftKey) { |
|
makeCallBack(this._previous)(evt); |
|
} else if (evt.keyCode === rightKey) { |
|
makeCallBack(this._next)(evt); |
|
} else if (evt.keyCode === spaceKey) { |
|
makeCallBack(this._playPauseToggle)(evt); |
|
} else if (evt.keyCode === fKey) { |
|
makeCallBack(this._fullScreenToggle)(evt); |
|
} else if (this._hasKeyBeenPressed(evt, zoomOutKeys)) { |
|
makeCallBack(this._zoomToOriginal)(evt); |
|
} else if (this._hasKeyBeenPressed(evt, zoomInKeys)) { |
|
makeCallBack(this._zoomToFit)(evt); |
|
} |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Determines if a key has been pressed by comparing the event and the key |
|
* |
|
* @param evt |
|
* @param {Array} keys |
|
* |
|
* @returns {boolean} |
|
* @private |
|
*/ |
|
_hasKeyBeenPressed: function (evt, keys) { |
|
var i, keysLength = keys.length; |
|
for (i = 0; i < keysLength; i++) { |
|
if (evt.keyCode === keys[i]) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Starts the slideshow timer |
|
* |
|
* @private |
|
*/ |
|
_setTimeout: function () { |
|
this._clearTimeout(); |
|
this.playTimeout = setTimeout(this._next.bind(this), this.interval); |
|
this.progressBar.stop(); |
|
this.progressBar.css('height', '6px'); |
|
this.progressBar.animate({'height': '26px'}, this.interval, 'linear'); |
|
}, |
|
|
|
/** |
|
* Stops the slideshow timer |
|
* |
|
* @private |
|
*/ |
|
_clearTimeout: function () { |
|
if (this.playTimeout) { |
|
clearTimeout(this.playTimeout); |
|
} |
|
this.progressBar.stop(); |
|
this.progressBar.css('height', '6px'); |
|
this.playTimeout = 0; |
|
}, |
|
|
|
/** |
|
* Starts/stops autoplay and shows/hides the play/pause buttons |
|
* |
|
* @private |
|
*/ |
|
_playPauseToggle: function () { |
|
if (this.playing === true) { |
|
this.playing = false; |
|
this._clearTimeout(); |
|
} else { |
|
this.playing = true; |
|
this._setTimeout(); |
|
} |
|
|
|
this.container.find('.play, .pause, .progress').toggleClass('hidden'); |
|
}, |
|
|
|
/** |
|
* Shows the next slide |
|
* |
|
* @private |
|
*/ |
|
_next: function () { |
|
if(Gallery.Share){ |
|
Gallery.Share.hideDropDown(); |
|
} |
|
this._setName(''); |
|
this.slideshow.hideErrorNotification(); |
|
this.zoomablePreview.reset(); |
|
|
|
if (this.errorLoadingImage) { |
|
this.current -= 1; |
|
} |
|
this.current = (this.current + 1) % this.images.length; |
|
var next = (this.current + 1) % this.images.length; |
|
this._updateSlideshow(next); |
|
}, |
|
|
|
/** |
|
* Shows the previous slide |
|
* |
|
* @private |
|
*/ |
|
_previous: function () { |
|
if(Gallery.Share){ |
|
Gallery.Share.hideDropDown(); |
|
} |
|
this._setName(''); |
|
this.slideshow.hideErrorNotification(); |
|
this.zoomablePreview.reset(); |
|
|
|
this.current = (this.current - 1 + this.images.length) % this.images.length; |
|
var previous = (this.current - 1 + this.images.length) % this.images.length; |
|
this._updateSlideshow(previous); |
|
}, |
|
|
|
/** |
|
* Asks the slideshow for the next image |
|
* |
|
* @param {number} imageId |
|
* @private |
|
*/ |
|
_updateSlideshow: function (imageId) { |
|
this.slideshow.next(this.current, this.images[imageId]); |
|
}, |
|
|
|
/** |
|
* Exits the slideshow by going back in history |
|
* |
|
* @private |
|
*/ |
|
_exit: function () { |
|
if (Gallery.Share){ |
|
Gallery.Share.hideDropDown(); |
|
} |
|
|
|
// Only modern browsers can manipulate history |
|
if (history && history.replaceState) { |
|
// We simulate a click on the back button in order to be consistent |
|
window.history.back(); |
|
} else { |
|
// For ancient browsers supported in core |
|
this.stop(); |
|
} |
|
}, |
|
|
|
/** |
|
* Launches fullscreen mode if the browser supports it |
|
* |
|
* @private |
|
*/ |
|
_fullScreenToggle: function () { |
|
this.zoomablePreview.fullScreenToggle(); |
|
}, |
|
|
|
/** |
|
* Resizes the image to its original size |
|
* |
|
* @private |
|
*/ |
|
_zoomToOriginal: function () { |
|
this.zoomablePreview.zoomToOriginal(); |
|
}, |
|
|
|
/** |
|
* Fits the image in the browser window |
|
* |
|
* @private |
|
*/ |
|
_zoomToFit: function () { |
|
this.zoomablePreview.zoomToFit(); |
|
}, |
|
|
|
/** |
|
* Sends the current image as a download |
|
* |
|
* @returns {boolean} |
|
* @private |
|
*/ |
|
_getImageDownload: function () { |
|
var downloadUrl = this.images[this.current].downloadUrl; |
|
|
|
return this.slideshow.getImageDownload(downloadUrl); |
|
}, |
|
|
|
/** |
|
* Changes the colour of the background of the image |
|
* |
|
* @private |
|
*/ |
|
_toggleBackground: function () { |
|
this.slideshow.toggleBackground(); |
|
}, |
|
|
|
/** |
|
* Shows the filename of the current image |
|
* @param {string} imageName |
|
* @private |
|
*/ |
|
_setName: function (imageName) { |
|
var nameElement = this.container.find('.title'); |
|
nameElement.text(imageName); |
|
}, |
|
|
|
/** |
|
* Delete the image from the slideshow |
|
* @private |
|
*/ |
|
_deleteImage: function () { |
|
var image = this.images[this.current]; |
|
var self = this; |
|
$.ajax({ |
|
type: 'DELETE', |
|
url: OC.getRootPath() + '/remote.php/webdav/' + image.path, |
|
success: function () { |
|
self.slideshow.deleteImage(image, self.current); |
|
self.images.splice(self.current, 1); |
|
if (self.images.length === 0) { |
|
self._exit(); |
|
} |
|
else { |
|
self.current = self.current % self.images.length; |
|
self._updateSlideshow(self.current); |
|
} |
|
} |
|
}); |
|
} |
|
}; |
|
|
|
SlideShow.Controls = Controls; |
|
})(jQuery, SlideShow); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global SlideShow, bigshot*/ |
|
(function ($, SlideShow, bigshot) { |
|
"use strict"; |
|
/** |
|
* Creates a zoomable preview |
|
* |
|
* @param {*} container |
|
* @constructor |
|
*/ |
|
var ZoomablePreview = function (container) { |
|
this.container = container; |
|
this.element = this.container.get(0); |
|
var bigshotContainer = container.find('.bigshotContainer'); |
|
this.bigshotElement = bigshotContainer.get(0); |
|
|
|
this._detectFullscreen(); |
|
this._setupControls(); |
|
|
|
$(window).resize(function () { |
|
this._zoomDecider(); |
|
}.bind(this)); |
|
}; |
|
|
|
ZoomablePreview.prototype = { |
|
container: null, |
|
element: null, |
|
bigshotContainer: null, |
|
bigshotElement: null, |
|
zoomable: null, |
|
fullScreen: null, |
|
canFullScreen: false, |
|
currentImage: null, |
|
mimeType: null, |
|
maxZoom: 3, |
|
smallImageDimension: 200 / window.devicePixelRatio, |
|
smallImageScale: 2, |
|
|
|
/** |
|
* Launches the Bigshot zoomable preview |
|
* |
|
* @param {*} image |
|
* @param {number} currentImage |
|
* @param {string} mimeType |
|
*/ |
|
startBigshot: function (image, currentImage, mimeType) { |
|
this.currentImage = currentImage; |
|
this.mimeType = mimeType; |
|
if (this.zoomable !== null) { |
|
this.zoomable.dispose(); |
|
this.zoomable = null; |
|
} |
|
var maxZoom = this.maxZoom; |
|
var imgWidth = image.naturalWidth / window.devicePixelRatio; |
|
var imgHeight = image.naturalHeight / window.devicePixelRatio; |
|
// Set arbitrary image dimension when we have a SVG |
|
if (imgWidth === 0 && mimeType === 'image/svg+xml') { |
|
imgWidth = 2048; |
|
imgHeight = 2048; |
|
} |
|
|
|
if (imgWidth < this.smallImageDimension && |
|
imgHeight < this.smallImageDimension && |
|
this.mimeType !== 'image/svg+xml') { |
|
maxZoom += 3; |
|
this.currentImage.isSmallImage = true; |
|
} |
|
this.zoomable = new bigshot.SimpleImage(new bigshot.ImageParameters({ |
|
container: this.bigshotElement, |
|
maxZoom: maxZoom, |
|
minZoom: 0, |
|
touchUI: false, |
|
width: imgWidth, |
|
height: imgHeight |
|
}), image); |
|
|
|
// Reset our zoom based on image and window dimensions. |
|
this._resetZoom(); |
|
|
|
// prevent zoom-on-doubleClick |
|
this.zoomable.addEventListener('dblclick', function (ie) { |
|
ie.preventDefault(); |
|
}); |
|
// Reset image position and size on orientation change |
|
var self = this; |
|
$(window).bind('orientationchange resize', function () { |
|
self._resetZoom(); |
|
}); |
|
}, |
|
|
|
/** |
|
* Resets the element for the next image to be displayed |
|
*/ |
|
reset: function () { |
|
if (this.zoomable !== null) { |
|
this.zoomable.stopFlying(); |
|
this._resetZoom(); |
|
} |
|
}, |
|
|
|
/** |
|
* Throws away the zoomable preview |
|
*/ |
|
stop: function () { |
|
if (this.fullScreen !== null) { |
|
this._fullScreenExit(); |
|
} |
|
if (this.zoomable !== null) { |
|
this.zoomable.dispose(); |
|
this.zoomable = null; |
|
} |
|
}, |
|
|
|
/** |
|
* Launches fullscreen mode if the browser supports it |
|
*/ |
|
fullScreenToggle: function () { |
|
if (this.zoomable === null) { |
|
return; |
|
} |
|
if (this.fullScreen !== null) { |
|
this._fullScreenExit(); |
|
} else { |
|
this._fullScreenStart(); |
|
} |
|
}, |
|
|
|
/** |
|
* Resizes the image to its original size |
|
*/ |
|
zoomToOriginal: function () { |
|
if (this.zoomable === null) { |
|
return; |
|
} |
|
if (this.currentImage.isSmallImage) { |
|
this.zoomable.flyTo(0, 0, this.smallImageScale, true); |
|
} else if ($(window).width() < this.zoomable.width || |
|
$(window).height() < this.zoomable.height) { |
|
// The image is larger than the window. |
|
// Set minimum zoom and call flyZoomToFit. |
|
this.zoomable.setMinZoom(this.zoomable.getZoomToFitValue()); |
|
this.zoomable.flyZoomToFit(); |
|
} else { |
|
this.zoomable.setMinZoom(0); |
|
this.zoomable.flyTo(0, 0, 0, true); |
|
} |
|
}, |
|
|
|
/** |
|
* Fits the image in the browser window |
|
*/ |
|
zoomToFit: function () { |
|
if (this.zoomable !== null) { |
|
this.zoomable.flyZoomToFit(); |
|
} |
|
}, |
|
|
|
/** |
|
* Detect fullscreen capability |
|
* @private |
|
*/ |
|
_detectFullscreen: function () { |
|
this.canFullScreen = this.element.requestFullscreen !== undefined || |
|
this.element.mozRequestFullScreen !== undefined || |
|
this.element.webkitRequestFullscreen !== undefined || |
|
this.element.msRequestFullscreen !== undefined; |
|
}, |
|
|
|
/** |
|
* Makes UI controls work on touchscreens. Pinch only works on iOS |
|
* @private |
|
*/ |
|
_setupControls: function () { |
|
var browser = new bigshot.Browser(); |
|
this.container.children('input').each(function (i, e) { |
|
browser.registerListener(e, 'click', browser.stopEventBubblingHandler(), false); |
|
browser.registerListener(e, 'touchstart', browser.stopEventBubblingHandler(), |
|
false); |
|
browser.registerListener(e, 'touchend', browser.stopEventBubblingHandler(), false); |
|
}); |
|
}, |
|
|
|
/** |
|
* Determines whether the image should be shown at its original size or if it should fill |
|
* the screen |
|
* |
|
* @private |
|
*/ |
|
_zoomDecider: function () { |
|
if (this.zoomable !== null) { |
|
if (this.fullScreen === null && this.mimeType !== 'image/svg+xml') { |
|
this.zoomToOriginal(); |
|
} else { |
|
this.zoomToFit(); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Resets the image to its original zoomed size |
|
* |
|
* @private |
|
*/ |
|
_resetZoom: function () { |
|
if (this.zoomable === null) { |
|
return; |
|
} |
|
if (this.currentImage.isSmallImage) { |
|
this.zoomable.setZoom(this.smallImageScale, true); |
|
} else if ($(window).width() < this.zoomable.width || |
|
$(window).height() < this.zoomable.height || |
|
this.fullScreen !== null || |
|
this.mimeType === 'image/svg+xml') { |
|
// The image is larger than the window, or we are fullScreen, |
|
// or this is an SVG. Set minimum zoom and call zoomToFit. |
|
this.zoomable.setMinZoom(this.zoomable.getZoomToFitValue()); |
|
this.zoomable.zoomToFit(); |
|
} else { |
|
// Zoom to the image size. |
|
this.zoomable.setMinZoom(0); |
|
this.zoomable.setZoom(0, true); |
|
} |
|
}, |
|
|
|
/** |
|
* Starts the fullscreen previews |
|
* |
|
* @private |
|
*/ |
|
_fullScreenStart: function () { |
|
if (!this.canFullScreen) { |
|
return; |
|
} |
|
this.fullScreen = new bigshot.FullScreen(this.element); |
|
this.fullScreen.open(); |
|
this.fullScreen.addOnClose(function () { |
|
this._fullScreenExit(); |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Stops the fullscreen previews |
|
* |
|
* @private |
|
*/ |
|
_fullScreenExit: function () { |
|
if (this.fullScreen === null) { |
|
return; |
|
} |
|
this.fullScreen.close(); |
|
this.fullScreen = null; |
|
this._zoomDecider(); |
|
|
|
} |
|
}; |
|
|
|
SlideShow.ZoomablePreview = ZoomablePreview; |
|
})(jQuery, SlideShow, bigshot); |
|
|
|
|
|
/** |
|
* Nextcloud - Gallery |
|
* |
|
* |
|
* This file is licensed under the Affero General Public License version 3 or |
|
* later. See the COPYING file. |
|
* |
|
* @author Olivier Paroz <galleryapps@oparoz.com> |
|
* |
|
* @copyright Olivier Paroz 2017 |
|
*/ |
|
/* global OC, OCA, FileList, $, t */ |
|
var GalleryButton = {}; |
|
GalleryButton.isPublic = false; |
|
GalleryButton.button = {}; |
|
GalleryButton.url = null; |
|
|
|
/** |
|
* Rebuilds the Gallery URL every time the files list has changed |
|
*/ |
|
GalleryButton.onFileListUpdated = function () { |
|
"use strict"; |
|
var fileList; |
|
|
|
if (GalleryButton.isPublic) { |
|
fileList = OCA.Sharing.PublicApp.fileList; |
|
} else { |
|
fileList = FileList; |
|
} |
|
|
|
GalleryButton.buildGalleryUrl(fileList.getCurrentDirectory().replace(/^\//, '')); |
|
}; |
|
|
|
/** |
|
* Builds the URL which will load the exact same folder in Gallery |
|
* |
|
* @param dir |
|
*/ |
|
GalleryButton.buildGalleryUrl = function (dir) { |
|
"use strict"; |
|
var params = {}; |
|
var tokenPath = ''; |
|
var sharingTokenElement = $('#sharingToken'); |
|
var token = (sharingTokenElement.val()) ? sharingTokenElement.val() : false; |
|
if (token) { |
|
params.token = token; |
|
tokenPath = 's/{token}'; |
|
} |
|
GalleryButton.url = |
|
OC.generateUrl('apps/gallery/' + tokenPath, params) + '#' + encodeURIComponent(dir); |
|
}; |
|
|
|
$(document).ready(function () { |
|
"use strict"; |
|
if ($('#body-login').length > 0) { |
|
return true; //deactivate on login page |
|
} |
|
|
|
if ($('html').is('.ie8')) { |
|
return true; //deactivate in IE8 |
|
} |
|
|
|
if ($('#isPublic').val()) { |
|
GalleryButton.isPublic = true; |
|
} |
|
|
|
if ($('#filesApp').val()) { |
|
|
|
$('#fileList').on('updated', GalleryButton.onFileListUpdated); |
|
|
|
// Button for opening files list as gallery view |
|
GalleryButton.button = |
|
$('<div id="gallery-button" class="button view-switcher">' + |
|
'<div id="button-loading" class="hidden"></div>' + |
|
'<span class="icon-toggle-pictures"></span>' + |
|
'</div>'); |
|
|
|
GalleryButton.button.click(function () { |
|
$(this).children('span').addClass('hidden'); |
|
$(this).children('#button-loading').removeClass('hidden').addClass('icon-loading-small'); |
|
window.location.href = GalleryButton.url; |
|
}); |
|
|
|
$('#controls').append(GalleryButton.button); |
|
} |
|
} |
|
); |
|
|
|
|
|
/* global Gallery, escapeHTML */ |
|
|
|
(function ($, Gallery) { |
|
"use strict"; |
|
|
|
/** |
|
* @typedef {Object} Gallery.Share.Types.ShareInfo |
|
* @property {Number} share_type |
|
* @property {Number} permissions |
|
* @property {Number} file_source optional |
|
* @property {Number} item_source |
|
* @property {String} token |
|
* @property {String} share_with |
|
* @property {String} share_with_displayname |
|
* @property {String} mail_send |
|
* @property {String} displayname_file_owner |
|
* @property {String} displayname_owner |
|
* @property {String} uid_owner |
|
* @property {String} uid_file_owner |
|
* @property {String} expiration optional |
|
* @property {Number} stime |
|
*/ |
|
|
|
// copied and stripped out from the old core |
|
var Share = { |
|
SHARE_TYPE_USER: 0, |
|
SHARE_TYPE_GROUP: 1, |
|
SHARE_TYPE_LINK: 3, |
|
SHARE_TYPE_EMAIL: 4, |
|
SHARE_TYPE_REMOTE: 6, |
|
|
|
/** |
|
* @deprecated use OC.Share.currentShares instead |
|
*/ |
|
itemShares: [], |
|
|
|
/** |
|
* Shares for the currently selected file. |
|
* (for which the dropdown is open) |
|
* |
|
* Key is item type and value is an array or |
|
* shares of the given item type. |
|
*/ |
|
currentShares: {}, |
|
|
|
/** |
|
* Whether the share dropdown is opened. |
|
*/ |
|
droppedDown: false, |
|
|
|
/** @type {object} **/ |
|
_lastSuggestions: undefined, |
|
|
|
/** @type {int} **/ |
|
_pendingOperationsCount: 0, |
|
|
|
/** |
|
* |
|
* @param path {String} path to the file/folder which should be shared |
|
* @param shareType {Number} 0 = user; 1 = group; 3 = public link; 6 = federated cloud |
|
* share |
|
* @param shareWith {String} user / group id with which the file should be shared |
|
* @param publicUpload {Boolean} allow public upload to a public shared folder |
|
* @param password {String} password to protect public link Share with |
|
* @param permissions {Number} 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 |
|
* = all (default: 31, for public shares: 1) |
|
* @param callback {Function} method to call back after a successful share creation |
|
* @param errorCallback {Function} method to call back after a failed share creation |
|
* |
|
* @returns {*} |
|
*/ |
|
share: function (path, shareType, shareWith, publicUpload, password, permissions, callback, errorCallback) { |
|
return $.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares' + '?format=json', |
|
type: 'POST', |
|
data: { |
|
path: path, |
|
shareType: shareType, |
|
shareWith: shareWith, |
|
publicUpload: publicUpload, |
|
password: password, |
|
permissions: permissions |
|
}, |
|
dataType: 'json' |
|
}).done(function (result) { |
|
if (callback) { |
|
callback(result.ocs.data); |
|
} |
|
}).fail(function (xhr) { |
|
var result = xhr.responseJSON; |
|
if (_.isFunction(errorCallback)) { |
|
errorCallback(result); |
|
} else { |
|
var msg = t('gallery', 'Error'); |
|
if (result.ocs && result.ocs.meta.message) { |
|
msg = result.ocs.meta.message; |
|
} |
|
OC.dialogs.alert(msg, t('gallery', 'Error while sharing')); |
|
} |
|
}); |
|
}, |
|
/** |
|
* |
|
* @param {Number} shareId |
|
* @param {Function} callback |
|
*/ |
|
unshare: function (shareId, callback) { |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'DELETE' |
|
}).done(function () { |
|
if (callback) { |
|
callback(); |
|
} |
|
}).fail(function () { |
|
OC.dialogs.alert(t('gallery', 'Error while unsharing'), t('gallery', 'Error')); |
|
|
|
}); |
|
}, |
|
/** |
|
* |
|
* @param {Number} shareId |
|
* @param {Number} permissions |
|
*/ |
|
setPermissions: function (shareId, permissions) { |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
permissions: permissions |
|
} |
|
}).fail(function () { |
|
OC.dialogs.alert(t('gallery', 'Error while changing permissions'), |
|
t('gallery', 'Error')); |
|
}); |
|
}, |
|
/** |
|
* |
|
* @param {String} searchTerm |
|
* @param {String} itemType |
|
*/ |
|
_getSuggestions: function (searchTerm, itemType) { |
|
if (this._lastSuggestions && |
|
this._lastSuggestions.searchTerm === searchTerm && |
|
this._lastSuggestions.itemType === itemType) { |
|
return this._lastSuggestions.promise; |
|
} |
|
|
|
var deferred = $.Deferred(); |
|
|
|
$.get(OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees', { |
|
format: 'json', |
|
search: searchTerm, |
|
perPage: 200, |
|
itemType: itemType |
|
}, function (result) { |
|
if (result.ocs.meta.statuscode === 100) { |
|
var filter = function(users) { |
|
var usersLength; |
|
|
|
var i; |
|
|
|
//Filter out the current user |
|
usersLength = users.length; |
|
for (i = 0; i < usersLength; i++) { |
|
if (users[i].value.shareWith === OC.currentUser) { |
|
users.splice(i, 1); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
filter(result.ocs.data.exact.users); |
|
|
|
var exactUsers = result.ocs.data.exact.users; |
|
var exactGroups = result.ocs.data.exact.groups; |
|
var exactRemotes = result.ocs.data.exact.remotes; |
|
var exactEmails = []; |
|
if (typeof(result.ocs.data.emails) !== 'undefined') { |
|
exactEmails = result.ocs.data.exact.emails; |
|
} |
|
var exactCircles = []; |
|
if (typeof(result.ocs.data.circles) !== 'undefined') { |
|
exactCircles = result.ocs.data.exact.circles; |
|
} |
|
|
|
var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactEmails).concat(exactCircles); |
|
|
|
filter(result.ocs.data.users); |
|
|
|
var users = result.ocs.data.users; |
|
var groups = result.ocs.data.groups; |
|
var remotes = result.ocs.data.remotes; |
|
var lookup = result.ocs.data.lookup; |
|
var emails = []; |
|
if (typeof(result.ocs.data.emails) !== 'undefined') { |
|
emails = result.ocs.data.emails; |
|
} |
|
var circles = []; |
|
if (typeof(result.ocs.data.circles) !== 'undefined') { |
|
circles = result.ocs.data.circles; |
|
} |
|
|
|
var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(emails).concat(circles).concat(lookup); |
|
|
|
deferred.resolve(suggestions, exactMatches); |
|
} else { |
|
deferred.reject(result.ocs.meta.message); |
|
} |
|
}).fail(function () { |
|
deferred.reject(); |
|
}); |
|
|
|
this._lastSuggestions = { |
|
searchTerm: searchTerm, |
|
itemType: itemType, |
|
promise: deferred.promise() |
|
}; |
|
|
|
return this._lastSuggestions.promise; |
|
}, |
|
/** |
|
* |
|
* @param {int} shareType |
|
* @param {int} possiblePermissions |
|
*/ |
|
_getPermissions: function (shareType, possiblePermissions) { |
|
// Default permissions are Edit (CRUD) and Share |
|
// Check if these permissions are possible |
|
var permissions = OC.PERMISSION_READ; |
|
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) { |
|
permissions = |
|
OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_READ; |
|
} else { |
|
if (possiblePermissions & OC.PERMISSION_UPDATE) { |
|
permissions = permissions | OC.PERMISSION_UPDATE; |
|
} |
|
if (possiblePermissions & OC.PERMISSION_CREATE) { |
|
permissions = permissions | OC.PERMISSION_CREATE; |
|
} |
|
if (possiblePermissions & OC.PERMISSION_DELETE) { |
|
permissions = permissions | OC.PERMISSION_DELETE; |
|
} |
|
if (oc_appconfig.core.resharingAllowed && |
|
(possiblePermissions & OC.PERMISSION_SHARE)) { |
|
permissions = permissions | OC.PERMISSION_SHARE; |
|
} |
|
} |
|
|
|
return permissions; |
|
}, |
|
/** |
|
* |
|
* @param {String} itemType |
|
* @param {String} path |
|
* @param {String} appendTo |
|
* @param {String} link |
|
* @param {Number} possiblePermissions |
|
* @param {String} filename |
|
*/ |
|
showDropDown: function (itemType, path, appendTo, link, possiblePermissions, filename) { |
|
// This is a sync AJAX request on the main thread... |
|
var data = this._loadShares(path); |
|
var dropDownEl; |
|
var self = this; |
|
var html = '<div id="dropdown" class="drop shareDropDown" data-item-type="' + escapeHTML(itemType) + |
|
'" data-item-source="' + escapeHTML(path) + '">'; |
|
if (data !== false && data[0] && !_.isUndefined(data[0].uid_file_owner) && |
|
data[0].uid_file_owner !== OC.currentUser |
|
) { |
|
html += '<span class="reshare">'; |
|
if (oc_config.enable_avatars === true) { |
|
html += '<div class="avatar"></div>'; |
|
} |
|
|
|
if (data[0].share_type == this.SHARE_TYPE_GROUP) { |
|
html += t('gallery', 'Shared with you and the group {group} by {owner}', { |
|
group: data[0].share_with, |
|
owner: data[0].displayname_owner |
|
}); |
|
} else { |
|
html += t('gallery', 'Shared with you by {owner}', |
|
{owner: data[0].displayname_owner}); |
|
} |
|
html += '</span><br />'; |
|
// reduce possible permissions to what the original share allowed |
|
possiblePermissions = possiblePermissions & data[0].permissions; |
|
} |
|
|
|
if (possiblePermissions & OC.PERMISSION_SHARE) { |
|
// Determine the Allow Public Upload status. |
|
// Used later on to determine if the |
|
// respective checkbox should be checked or |
|
// not. |
|
var publicUploadEnabled = $('#gallery').data('allow-public-upload'); |
|
if (typeof publicUploadEnabled == 'undefined') { |
|
publicUploadEnabled = 'no'; |
|
} |
|
var allowPublicUploadStatus = false; |
|
|
|
$.each(data, function (key, value) { |
|
if (value.share_type === self.SHARE_TYPE_LINK) { |
|
allowPublicUploadStatus = |
|
(value.permissions & OC.PERMISSION_CREATE) ? true : false; |
|
return true; |
|
} |
|
}); |
|
|
|
var sharePlaceholder = t('gallery', 'Share with users or groups …'); |
|
if (oc_appconfig.core.remoteShareAllowed) { |
|
sharePlaceholder = t('gallery', 'Share with users, groups or remote users …'); |
|
} |
|
|
|
html += '<label for="shareWith" class="hidden-visually">' + t('gallery', 'Share') + |
|
'</label>'; |
|
html += |
|
'<input id="shareWith" type="text" placeholder="' + sharePlaceholder + '" />'; |
|
html += '<span class="shareWithConfirm icon-confirm svg"></span>'; |
|
html += '<span class="shareWithLoading icon-loading-small hidden"></span>'; |
|
html += '<ul id="shareWithList">'; |
|
html += '</ul>'; |
|
var linksAllowed = $('#allowShareWithLink').val() === 'yes'; |
|
if (link && linksAllowed) { |
|
html += '<div id="link" class="linkShare">'; |
|
html += '<span class="icon-loading-small hidden"></span>'; |
|
html += |
|
'<input type="checkbox" class="checkbox checkbox--right" ' + |
|
'name="linkCheckbox" id="linkCheckbox" value="1" />' + |
|
'<label for="linkCheckbox">' + t('gallery', 'Share link') + '</label>'; |
|
html += '<br />'; |
|
|
|
var defaultExpireMessage = ''; |
|
if ((itemType === 'folder' || itemType === 'file') && |
|
oc_appconfig.core.defaultExpireDateEnforced) { |
|
defaultExpireMessage = |
|
t('gallery', |
|
'The public link will expire no later than {days} days after it is created', |
|
{'days': oc_appconfig.core.defaultExpireDate}) + '<br/>'; |
|
} |
|
|
|
html += '<label for="linkText" class="hidden-visually">' + t('gallery', 'Link') + |
|
'</label>'; |
|
html += '<div id="linkText-container">'; |
|
html += '<input id="linkText" type="text" readonly="readonly" />'; |
|
html += '<a id="linkTextMore" class="button icon-more" href="#"></a>'; |
|
html += '<div id="linkSocial" class="popovermenu bubble menu hidden"></div>'; |
|
html += '</div>'; |
|
|
|
html += |
|
'<input type="checkbox" class="checkbox checkbox--right" ' + |
|
'name="showPassword" id="showPassword" value="1" />' + |
|
'<label for="showPassword" style="display:none;">' + |
|
t('gallery', 'Password protect') + '</label>'; |
|
html += '<div id="linkPass">'; |
|
html += '<label for="linkPassText" class="hidden-visually">' + |
|
t('gallery', 'Password') + '</label>'; |
|
html += '<input id="linkPassText" type="password" placeholder="' + |
|
t('gallery', 'Choose a password for the public link') + '" />'; |
|
html += '<span class="icon-loading-small hidden"></span>'; |
|
html += '</div>'; |
|
|
|
if (itemType === 'folder' && (possiblePermissions & OC.PERMISSION_CREATE) && |
|
publicUploadEnabled === 'yes') { |
|
html += '<div id="allowPublicUploadWrapper" style="display:none;">'; |
|
html += '<span class="icon-loading-small hidden"></span>'; |
|
html += |
|
'<input type="checkbox" class="checkbox checkbox--right" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload"' + |
|
((allowPublicUploadStatus) ? 'checked="checked"' : '') + ' />'; |
|
html += '<label for="sharingDialogAllowPublicUpload">' + |
|
t('gallery', 'Allow editing') + '</label>'; |
|
html += '</div>'; |
|
} |
|
|
|
var mailPublicNotificationEnabled = $('input:hidden[name=mailPublicNotificationEnabled]').val(); |
|
if (mailPublicNotificationEnabled === 'yes') { |
|
html += '<form id="emailPrivateLink">'; |
|
html += |
|
'<input id="email" style="display:none; width:62%;" value="" placeholder="' + |
|
t('gallery', 'Email link to person') + '" type="text" />'; |
|
html += |
|
'<input id="emailButton" style="display:none;" type="submit" value="' + |
|
t('gallery', 'Send') + '" />'; |
|
html += '</form>'; |
|
} |
|
} |
|
|
|
html += '<div id="expiration">'; |
|
html += |
|
'<input type="checkbox" class="checkbox checkbox--right" ' + |
|
'name="expirationCheckbox" id="expirationCheckbox" value="1" />' + |
|
'<label for="expirationCheckbox">' + |
|
t('gallery', 'Set expiration date') + '</label>'; |
|
html += '<label for="expirationDate" class="hidden-visually">' + |
|
t('gallery', 'Expiration') + '</label>'; |
|
html += '<input id="expirationDate" type="text" placeholder="' + |
|
t('gallery', 'Expiration date') + '" style="display:none; width:90%;" />'; |
|
html += '<em id="defaultExpireMessage">' + defaultExpireMessage + '</em>'; |
|
html += '</div>'; |
|
dropDownEl = $(html); |
|
dropDownEl = dropDownEl.appendTo(appendTo); |
|
|
|
//Get owner avatars |
|
if (oc_config.enable_avatars === true && data !== false && data[0] !== false && |
|
!_.isUndefined(data[0]) && !_.isUndefined(data[0].uid_file_owner)) { |
|
dropDownEl.find(".avatar").avatar(data[0].uid_file_owner, 32); |
|
} |
|
|
|
// Reset item shares |
|
this.itemShares = []; |
|
this.currentShares = {}; |
|
if (data) { |
|
$.each(data, function (index, share) { |
|
if (share.share_type === self.SHARE_TYPE_LINK) { |
|
self.showLink(share.id, share.token, share.share_with); |
|
} else { |
|
if (share.share_with !== OC.currentUser) { |
|
if (share.share_type === self.SHARE_TYPE_REMOTE) { |
|
self._addShareWith(share.id, |
|
share.share_type, |
|
share.share_with, |
|
share.share_with_displayname, |
|
share.permissions, |
|
OC.PERMISSION_READ | OC.PERMISSION_UPDATE | |
|
OC.PERMISSION_CREATE, |
|
share.mail_send, |
|
false); |
|
} else { |
|
self._addShareWith(share.id, |
|
share.share_type, |
|
share.share_with, |
|
share.share_with_displayname, |
|
share.permissions, |
|
possiblePermissions, |
|
share.mail_send, |
|
false); |
|
} |
|
} |
|
} |
|
if (share.expiration != null) { |
|
var expireDate = moment(share.expiration, 'YYYY-MM-DD').format( |
|
'DD-MM-YYYY'); |
|
self.showExpirationDate(expireDate, share.stime); |
|
} |
|
}); |
|
} |
|
$('#shareWith').autocomplete({ |
|
minLength: 2, |
|
delay: 750, |
|
source: function (search, response) { |
|
var $shareWithField = $('#dropdown #shareWith'); |
|
var $loading = $('#dropdown .shareWithLoading'); |
|
var $confirm = $('#dropdown .shareWithConfirm'); |
|
$loading.removeClass('hidden'); |
|
$confirm.addClass('hidden'); |
|
self._pendingOperationsCount++; |
|
|
|
$shareWithField.removeClass('error') |
|
.tooltip('hide'); |
|
|
|
self._getSuggestions( |
|
search.term.trim(), |
|
itemType |
|
).done(function(suggestions) { |
|
self._pendingOperationsCount--; |
|
if (self._pendingOperationsCount === 0) { |
|
$loading.addClass('hidden'); |
|
$confirm.removeClass('hidden'); |
|
} |
|
|
|
if (suggestions.length > 0) { |
|
$('#shareWith') |
|
.autocomplete("option", "autoFocus", true); |
|
|
|
response(suggestions); |
|
|
|
// show a notice that the list is truncated |
|
// this is the case if one of the search results is at least as long as the max result config option |
|
if (oc_config['sharing.maxAutocompleteResults'] > 0 && |
|
Math.min(perPage, oc_config['sharing.maxAutocompleteResults']) |
|
<= Math.max(users.length, groups.length, remotes.length, emails.length, lookup.length)) { |
|
|
|
var message = t('gallery', 'This list is maybe truncated - please refine your search term to see more results.'); |
|
$('.ui-autocomplete').append('<li class="autocomplete-note">' + message + '</li>'); |
|
} |
|
|
|
} else { |
|
var title = t('gallery', 'No users or groups found for {search}', {search: $('#shareWith').val()}); |
|
if (!oc_appconfig.core.allowGroupSharing) { |
|
title = t('gallery', 'No users found for {search}', {search: $('#shareWith').val()}); |
|
} |
|
$('#shareWith').addClass('error') |
|
.attr('data-original-title', title) |
|
.tooltip('hide') |
|
.tooltip({ |
|
placement: 'bottom', |
|
trigger: 'manual' |
|
}) |
|
.tooltip('fixTitle') |
|
.tooltip('show'); |
|
response(); |
|
} |
|
}).fail(function (message) { |
|
self._pendingOperationsCount--; |
|
if (self._pendingOperationsCount === 0) { |
|
$('#dropdown').find('.shareWithLoading').addClass('hidden'); |
|
$('#dropdown').find('.shareWithConfirm').removeClass('hidden'); |
|
} |
|
|
|
if (message) { |
|
OC.Notification.showTemporary(t('gallery', 'An error occurred ("{message}"). Please try again', { message: message })); |
|
} else { |
|
OC.Notification.showTemporary(t('gallery', 'An error occurred. Please try again')); |
|
} |
|
}); |
|
}, |
|
focus: function (event) { |
|
event.preventDefault(); |
|
}, |
|
select: function (event, selected) { |
|
// Ensure that the keydown handler for the input field |
|
// is not called; otherwise it would try to add the |
|
// recipient again, which would fail. |
|
event.stopImmediatePropagation(); |
|
var $dropDown = $('#dropdown'); |
|
var itemSource = $dropDown.data('item-source'); |
|
var expirationDate = ''; |
|
if ($('#expirationCheckbox').is(':checked') === true) { |
|
expirationDate = $("#expirationDate").val(); |
|
} |
|
var shareType = selected.item.value.shareType; |
|
var shareWith = selected.item.value.shareWith; |
|
$(this).val(shareWith); |
|
var permissions = self._getPermissions(shareType, possiblePermissions); |
|
|
|
var $input = $(this); |
|
var $loading = $dropDown.find('.shareWithLoading'); |
|
var $confirm = $dropDown.find('.shareWithConfirm'); |
|
$loading.removeClass('hidden'); |
|
$confirm.addClass('hidden'); |
|
$input.val(t('gallery', 'Adding user...')); |
|
$input.prop('disabled', true); |
|
self._pendingOperationsCount++; |
|
Gallery.Share.share( |
|
itemSource, |
|
shareType, |
|
shareWith, |
|
0, |
|
null, |
|
permissions, |
|
function (data) { |
|
// Adding a share changes the suggestions. |
|
self._lastSuggestions = undefined; |
|
|
|
var posPermissions = possiblePermissions; |
|
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) { |
|
posPermissions = permissions; |
|
} |
|
Gallery.Share._addShareWith(data.id, shareType, shareWith, |
|
selected.item.label, |
|
permissions, posPermissions); |
|
|
|
$input.val(''); |
|
$input.prop('disabled', false); |
|
|
|
self._pendingOperationsCount--; |
|
if (self._pendingOperationsCount === 0) { |
|
$loading.addClass('hidden'); |
|
$confirm.removeClass('hidden'); |
|
} |
|
}, |
|
function (result) { |
|
var message = t('gallery', 'Error'); |
|
if (result && result.ocs && result.ocs.meta && result.ocs.meta.message) { |
|
message = result.ocs.meta.message; |
|
} |
|
OC.Notification.showTemporary(message); |
|
|
|
$input.val(shareWith); |
|
$input.prop('disabled', false); |
|
|
|
self._pendingOperationsCount--; |
|
if (self._pendingOperationsCount === 0) { |
|
$loading.addClass('hidden'); |
|
$confirm.removeClass('hidden'); |
|
} |
|
}); |
|
return false; |
|
} |
|
}).data("ui-autocomplete")._renderItem = function (ul, item) { |
|
// customize internal _renderItem function to display groups and users |
|
// differently |
|
var insert = $("<a>"); |
|
var text = item.label; |
|
if (item.value.shareType === Gallery.Share.SHARE_TYPE_GROUP) { |
|
text = text + ' (' + t('gallery', 'group') + ')'; |
|
} else if (item.value.shareType === Gallery.Share.SHARE_TYPE_REMOTE) { |
|
text = text + ' (' + t('gallery', 'remote') + ')'; |
|
} |
|
insert.text(text); |
|
if (item.value.shareType === Gallery.Share.SHARE_TYPE_GROUP) { |
|
insert = insert.wrapInner('<strong></strong>'); |
|
} |
|
return $("<li>") |
|
.addClass( |
|
(item.value.shareType === |
|
Gallery.Share.SHARE_TYPE_GROUP) ? 'group' : 'user') |
|
.append(insert) |
|
.appendTo(ul); |
|
}; |
|
|
|
var shareFieldKeydownHandler = function(event) { |
|
if (event.keyCode !== 13) { |
|
return true; |
|
} |
|
|
|
self._confirmShare(itemType, possiblePermissions); |
|
|
|
return false; |
|
}; |
|
|
|
$('#shareWith').on('keydown', shareFieldKeydownHandler); |
|
|
|
$('#shareWith').on('input', function () { |
|
if ($(this).val().length < 2) { |
|
$(this).removeClass('error').tooltip('hide'); |
|
} |
|
}); |
|
|
|
/* trigger search after the field was re-selected */ |
|
$('#shareWith').on('focus', function() { |
|
$(this).autocomplete('search'); |
|
}); |
|
|
|
$('.shareWithConfirm').on('click', function () { |
|
self._confirmShare(itemType, possiblePermissions); |
|
}); |
|
|
|
if (link && linksAllowed && $('#email').length != 0) { |
|
$('#email').autocomplete({ |
|
minLength: 1, |
|
source: function (search, response) { |
|
$.get(OC.filePath('core', 'ajax', 'share.php'), { |
|
fetch: 'getShareWithEmail', |
|
search: search.term |
|
}, function (result) { |
|
if (result.status == 'success' && result.data.length > 0) { |
|
response(result.data); |
|
} |
|
}); |
|
}, |
|
select: function (event, item) { |
|
$('#email').val(item.item.email); |
|
return false; |
|
} |
|
}) |
|
.data("ui-autocomplete")._renderItem = function (ul, item) { |
|
return $('<li>') |
|
.append('<a>' + escapeHTML(item.displayname) + "<br>" + |
|
escapeHTML(item.email) + '</a>') |
|
.appendTo(ul); |
|
}; |
|
} |
|
|
|
} else { |
|
html += '<input id="shareWith" type="text" placeholder="' + |
|
t('gallery', 'Resharing is not allowed') + |
|
'" style="width:90%;" disabled="disabled"/>'; |
|
html += '</div>'; |
|
dropDownEl = $(html); |
|
dropDownEl.appendTo(appendTo); |
|
} |
|
dropDownEl.attr('data-item-source-name', filename); |
|
$('#dropdown').slideDown(OC.menuSpeed, function () { |
|
Gallery.Share.droppedDown = true; |
|
}); |
|
if ($('html').hasClass('lte9')) { |
|
$('#dropdown input[placeholder]').placeholder(); |
|
} |
|
$('#shareWith').focus(); |
|
}, |
|
_confirmShare: function (itemType, possiblePermissions) { |
|
var self = this; |
|
var $shareWithField = $('#dropdown #shareWith'); |
|
var $loading = $('#dropdown .shareWithLoading'); |
|
var $confirm = $('#dropdown .shareWithConfirm'); |
|
|
|
$loading.removeClass('hidden'); |
|
$confirm.addClass('hidden'); |
|
this._pendingOperationsCount++; |
|
|
|
$shareWithField.prop('disabled', true); |
|
|
|
// Disabling the autocompletion does not clear its search timeout; |
|
// removing the focus from the input field does, but only if the |
|
// autocompletion is not disabled when the field loses the focus. |
|
// Thus, the field has to be disabled before disabling the |
|
// autocompletion to prevent an old pending search result from |
|
// being processed once the field is enabled again. |
|
$shareWithField.autocomplete('close'); |
|
$shareWithField.autocomplete('disable'); |
|
|
|
var itemSource = $('#dropdown').data('item-source'); |
|
var expirationDate = ''; |
|
if ($('#expirationCheckbox').is(':checked') === true) { |
|
expirationDate = $("#expirationDate").val(); |
|
} |
|
|
|
var restoreUI = function() { |
|
self._pendingOperationsCount--; |
|
if (self._pendingOperationsCount === 0) { |
|
$loading.addClass('hidden'); |
|
$confirm.removeClass('hidden'); |
|
} |
|
|
|
$shareWithField.prop('disabled', false); |
|
$shareWithField.focus(); |
|
}; |
|
|
|
this._getSuggestions( |
|
$shareWithField.val(), |
|
itemType |
|
).done(function(suggestions, exactMatches) { |
|
if (suggestions.length === 0) { |
|
restoreUI(); |
|
|
|
$shareWithField.autocomplete('enable'); |
|
|
|
// There is no need to show an error message here; it will |
|
// be automatically shown when the autocomplete is activated |
|
// again (due to the focus on the field) and it finds no |
|
// matches. |
|
|
|
return; |
|
} |
|
|
|
if (exactMatches.length !== 1) { |
|
restoreUI(); |
|
|
|
$shareWithField.autocomplete('enable'); |
|
|
|
return; |
|
} |
|
|
|
var shareType = exactMatches[0].value.shareType; |
|
var shareWith = exactMatches[0].value.shareWith; |
|
var permissions = self._getPermissions(shareType, possiblePermissions); |
|
|
|
var actionSuccess = function(data) { |
|
var updatedPossiblePermissions = possiblePermissions; |
|
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) { |
|
updatedPossiblePermissions = permissions; |
|
} |
|
Gallery.Share._addShareWith(data.id, shareType, shareWith, |
|
exactMatches[0].label, |
|
permissions, updatedPossiblePermissions); |
|
|
|
// Adding a share changes the suggestions. |
|
self._lastSuggestions = undefined; |
|
|
|
$shareWithField.val(''); |
|
|
|
restoreUI(); |
|
|
|
$shareWithField.autocomplete('enable'); |
|
}; |
|
|
|
var actionError = function(result) { |
|
restoreUI(); |
|
|
|
$shareWithField.autocomplete('enable'); |
|
|
|
var message = t('gallery', 'Error'); |
|
if (result && result.ocs && result.ocs.meta && result.ocs.meta.message) { |
|
message = result.ocs.meta.message; |
|
} |
|
OC.Notification.showTemporary(message); |
|
}; |
|
|
|
Gallery.Share.share( |
|
itemSource, |
|
shareType, |
|
shareWith, |
|
0, |
|
null, |
|
permissions, |
|
actionSuccess, |
|
actionError |
|
); |
|
}).fail(function (message) { |
|
restoreUI(); |
|
|
|
$shareWithField.autocomplete('enable'); |
|
|
|
// There is no need to show an error message here; it will be |
|
// automatically shown when the autocomplete is activated again |
|
// (due to the focus on the field) and getting the suggestions |
|
// fail. |
|
}); |
|
}, |
|
/** |
|
* |
|
* @param callback |
|
*/ |
|
hideDropDown: function (callback) { |
|
this.currentShares = null; |
|
$('#dropdown').slideUp(OC.menuSpeed, function () { |
|
Gallery.Share.droppedDown = false; |
|
$('#dropdown').remove(); |
|
if (typeof FileActions !== 'undefined') { |
|
$('tr').removeClass('mouseOver'); |
|
} |
|
if (callback) { |
|
callback.call(); |
|
} |
|
}); |
|
}, |
|
/** |
|
* |
|
* @param id |
|
* @param token |
|
* @param password |
|
*/ |
|
showLink: function (id, token, password) { |
|
var $linkCheckbox = $('#linkCheckbox'); |
|
this.itemShares[this.SHARE_TYPE_LINK] = true; |
|
$linkCheckbox.attr('checked', true); |
|
$linkCheckbox.data('id', id); |
|
var $linkText = $('#linkText'); |
|
|
|
if (Gallery.appName === 'files') { |
|
var link = parent.location.protocol + '//' + location.host + |
|
OC.generateUrl('/s/') + token; |
|
} else { |
|
var link = parent.location.protocol + '//' + location.host + |
|
OC.generateUrl('/apps/gallery/s/') + token; |
|
} |
|
|
|
$linkText.val(link); |
|
$linkText.slideDown(OC.menuSpeed); |
|
$linkText.css('display', 'block'); |
|
if (oc_appconfig.core.enforcePasswordForPublicLink === false || password === null) { |
|
$('#showPassword+label').show(); |
|
} |
|
if (password != null) { |
|
$('#linkPass').slideDown(OC.menuSpeed); |
|
$('#showPassword').attr('checked', true); |
|
$('#linkPassText').attr('placeholder', '**********'); |
|
} |
|
$('#expiration').show(); |
|
$('#emailPrivateLink #email').show(); |
|
$('#emailPrivateLink #emailButton').show(); |
|
$('#allowPublicUploadWrapper').show(); |
|
$('#linkTextMore').show(); |
|
$('#linkSocial').hide(); |
|
$('#linkSocial').html(''); |
|
|
|
var ul = $('<ul/>'); |
|
|
|
OC.Share.Social.Collection.each(function(model) { |
|
var url = model.get('url'); |
|
url = url.replace('{{reference}}', link); |
|
|
|
var li = $('<li>' + |
|
'<a href="#" class="menuitem pop-up" data-url="' + url + '" data-window="'+model.get('newWindow')+'">' + |
|
'<span class="icon ' + model.get('iconClass') + '"></span>' + |
|
'<span>' + model.get('name') + '</span>' + |
|
'</a>'); |
|
li.appendTo(ul); |
|
}); |
|
ul.appendTo('#linkSocial'); |
|
|
|
if (OC.Share.Social.Collection.length === 0) { |
|
$('#linkTextMore').hide(); |
|
$linkText.addClass('no-menu-item'); |
|
} else { |
|
$linkText.removeClass('no-menu-item'); |
|
} |
|
}, |
|
/** |
|
* |
|
*/ |
|
hideLink: function () { |
|
$('#linkText').slideUp(OC.menuSpeed); |
|
$('#defaultExpireMessage').hide(); |
|
$('#showPassword+label').hide(); |
|
$('#linkSocial').hide(); |
|
$('#linkTextMore').hide(); |
|
$('#linkPass').slideUp(OC.menuSpeed); |
|
$('#emailPrivateLink #email').hide(); |
|
$('#emailPrivateLink #emailButton').hide(); |
|
$('#allowPublicUploadWrapper').hide(); |
|
}, |
|
/** |
|
* Displays the expiration date field |
|
* |
|
* @param {String} date current expiration date |
|
* @param {Date|Number|String} [shareTime] share timestamp in seconds, defaults to now |
|
*/ |
|
showExpirationDate: function (date, shareTime) { |
|
var $expirationDate = $('#expirationDate'); |
|
var $expirationCheckbox = $('#expirationCheckbox'); |
|
var now = new Date(); |
|
// min date should always be the next day |
|
var minDate = new Date(); |
|
minDate.setDate(minDate.getDate() + 1); |
|
var datePickerOptions = { |
|
minDate: minDate, |
|
maxDate: null |
|
}; |
|
// TODO: hack: backend returns string instead of integer |
|
shareTime = this._parseTime(shareTime); |
|
if (_.isNumber(shareTime)) { |
|
shareTime = new Date(shareTime * 1000); |
|
} |
|
if (!shareTime) { |
|
shareTime = now; |
|
} |
|
$expirationCheckbox.attr('checked', true); |
|
$expirationDate.val(date); |
|
$expirationDate.slideDown(OC.menuSpeed); |
|
$expirationDate.css('display', 'block'); |
|
$expirationDate.datepicker({ |
|
dateFormat: 'dd-mm-yy' |
|
}); |
|
if (oc_appconfig.core.defaultExpireDateEnforced) { |
|
$expirationCheckbox.attr('disabled', true); |
|
shareTime = OC.Util.stripTime(shareTime).getTime(); |
|
// max date is share date + X days |
|
datePickerOptions.maxDate = |
|
new Date(shareTime + oc_appconfig.core.defaultExpireDate * 24 * 3600 * 1000); |
|
} |
|
if (oc_appconfig.core.defaultExpireDateEnabled) { |
|
$('#defaultExpireMessage').slideDown(OC.menuSpeed); |
|
} |
|
$.datepicker.setDefaults(datePickerOptions); |
|
}, |
|
/** |
|
* Get the default Expire date |
|
* |
|
* @return {String} The expire date |
|
*/ |
|
getDefaultExpirationDate: function () { |
|
var expireDateString = ''; |
|
if (oc_appconfig.core.defaultExpireDateEnabled) { |
|
var date = new Date().getTime(); |
|
var expireAfterMs = oc_appconfig.core.defaultExpireDate * 24 * 60 * 60 * 1000; |
|
var expireDate = new Date(date + expireAfterMs); |
|
var month = expireDate.getMonth() + 1; |
|
var year = expireDate.getFullYear(); |
|
var day = expireDate.getDate(); |
|
expireDateString = year + "-" + month + '-' + day + ' 00:00:00'; |
|
} |
|
return expireDateString; |
|
}, |
|
/** |
|
* Loads all shares associated with a path |
|
* |
|
* @param path |
|
* |
|
* @returns {Gallery.Share.Types.ShareInfo|Boolean} |
|
* @private |
|
*/ |
|
_loadShares: function (path) { |
|
var data = false; |
|
var url = OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares' + '?format=json'; |
|
$.ajax({ |
|
url: url, |
|
type: 'GET', |
|
data: { |
|
path: path, |
|
shared_with_me: true |
|
}, |
|
async: false |
|
}).done(function (result) { |
|
data = result.ocs.data; |
|
$.ajax({ |
|
url: url, |
|
type: 'GET', |
|
data: { |
|
path: path, |
|
reshares: true |
|
}, |
|
async: false |
|
}).done(function (result) { |
|
data = _.union(data, result.ocs.data); |
|
}) |
|
|
|
}); |
|
|
|
if (data === false) { |
|
OC.dialogs.alert(t('gallery', 'Error while retrieving shares'), |
|
t('gallery', 'Error')); |
|
} |
|
|
|
return data; |
|
}, |
|
/** |
|
* |
|
* @param shareId |
|
* @param shareType |
|
* @param shareWith |
|
* @param shareWithDisplayName |
|
* @param permissions |
|
* @param possiblePermissions |
|
* @param mailSend |
|
* |
|
* @private |
|
*/ |
|
_addShareWith: function (shareId, shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend) { |
|
var shareItem = { |
|
share_id: shareId, |
|
share_type: shareType, |
|
share_with: shareWith, |
|
share_with_displayname: shareWithDisplayName, |
|
permissions: permissions |
|
}; |
|
if (shareType === this.SHARE_TYPE_GROUP) { |
|
shareWithDisplayName = shareWithDisplayName + " (" + t('gallery', 'group') + ')'; |
|
} |
|
if (shareType === this.SHARE_TYPE_REMOTE) { |
|
shareWithDisplayName = shareWithDisplayName + " (" + t('gallery', 'remote') + ')'; |
|
} |
|
if (!this.itemShares[shareType]) { |
|
this.itemShares[shareType] = []; |
|
} |
|
this.itemShares[shareType].push(shareWith); |
|
|
|
var editChecked = '', |
|
createChecked = '', |
|
updateChecked = '', |
|
deleteChecked = '', |
|
shareChecked = ''; |
|
if (permissions & OC.PERMISSION_CREATE) { |
|
createChecked = 'checked="checked"'; |
|
editChecked = 'checked="checked"'; |
|
} |
|
if (permissions & OC.PERMISSION_UPDATE) { |
|
updateChecked = 'checked="checked"'; |
|
editChecked = 'checked="checked"'; |
|
} |
|
if (permissions & OC.PERMISSION_DELETE) { |
|
deleteChecked = 'checked="checked"'; |
|
editChecked = 'checked="checked"'; |
|
} |
|
if (permissions & OC.PERMISSION_SHARE) { |
|
shareChecked = 'checked="checked"'; |
|
} |
|
var html = '<li style="clear: both;" ' + |
|
'data-id="' + escapeHTML(shareId) + '"' + |
|
'data-share-type="' + escapeHTML(shareType) + '"' + |
|
'data-share-with="' + escapeHTML(shareWith) + '"' + |
|
'title="' + escapeHTML(shareWith) + '">'; |
|
var showCrudsButton; |
|
html += |
|
'<a href="#" class="unshare"><img class="svg" alt="' + t('gallery', 'Unshare') + |
|
'" title="' + t('gallery', 'Unshare') + '" src="' + |
|
OC.imagePath('core', 'actions/delete') + '"/></a>'; |
|
if (oc_config.enable_avatars === true) { |
|
html += '<div class="avatar"></div>'; |
|
} |
|
html += '<span class="username">' + escapeHTML(shareWithDisplayName) + '</span>'; |
|
var mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val(); |
|
if (mailNotificationEnabled === 'yes' && |
|
shareType !== this.SHARE_TYPE_REMOTE) { |
|
var checked = ''; |
|
if (mailSend === 1) { |
|
checked = 'checked'; |
|
} |
|
html += |
|
'<input id="mail-' + escapeHTML(shareWith) + '" type="checkbox" class="mailNotification checkbox checkbox--right" ' + |
|
'name="mailNotification" ' + |
|
checked + ' />'; |
|
html += |
|
'<label for="mail-' + escapeHTML(shareWith) + '">' + t('gallery', 'notify by email') + '</label>'; |
|
} |
|
if (oc_appconfig.core.resharingAllowed && |
|
(possiblePermissions & OC.PERMISSION_SHARE)) { |
|
html += '<input id="canShare-' + escapeHTML(shareWith) + |
|
'" type="checkbox" class="permissions checkbox checkbox--right" name="share" ' + |
|
shareChecked + ' data-permissions="' + OC.PERMISSION_SHARE + '" />'; |
|
html += '<label for="canShare-' + escapeHTML(shareWith) + '">' + |
|
t('gallery', 'can share') + '</label>'; |
|
} |
|
if (possiblePermissions & OC.PERMISSION_CREATE || |
|
possiblePermissions & OC.PERMISSION_UPDATE || |
|
possiblePermissions & OC.PERMISSION_DELETE) { |
|
html += '<input id="canEdit-' + escapeHTML(shareWith) + |
|
'" type="checkbox" class="permissions checkbox checkbox--right" name="edit" ' + |
|
editChecked + ' />'; |
|
html += '<label for="canEdit-' + escapeHTML(shareWith) + '">' + |
|
t('gallery', 'can edit') + '</label>'; |
|
} |
|
if (shareType !== this.SHARE_TYPE_REMOTE) { |
|
showCrudsButton = '<a href="#" class="showCruds"><img class="svg" alt="' + |
|
t('gallery', 'access control') + '" src="' + |
|
OC.imagePath('core', 'actions/triangle-s') + '"/></a>'; |
|
} |
|
html += '<div class="cruds" style="display:none;">'; |
|
if (possiblePermissions & OC.PERMISSION_CREATE) { |
|
html += '<input id="canCreate-' + escapeHTML(shareWith) + |
|
'" type="checkbox" class="permissions checkbox checkbox--right" name="create" ' + |
|
createChecked + ' data-permissions="' + OC.PERMISSION_CREATE + '"/>'; |
|
html += '<label for="canCreate-' + escapeHTML(shareWith) + '">' + |
|
t('gallery', 'create') + '</label>'; |
|
} |
|
if (possiblePermissions & OC.PERMISSION_UPDATE) { |
|
html += '<input id="canUpdate-' + escapeHTML(shareWith) + |
|
'" type="checkbox" class="permissions checkbox checkbox--right" name="update" ' + |
|
updateChecked + ' data-permissions="' + OC.PERMISSION_UPDATE + '"/>'; |
|
html += '<label for="canUpdate-' + escapeHTML(shareWith) + '">' + |
|
t('gallery', 'change') + '</label>'; |
|
} |
|
if (possiblePermissions & OC.PERMISSION_DELETE) { |
|
html += '<input id="canDelete-' + escapeHTML(shareWith) + |
|
'" type="checkbox" class="permissions checkbox checkbox--right" name="delete" ' + |
|
deleteChecked + ' data-permissions="' + OC.PERMISSION_DELETE + '"/>'; |
|
html += '<label for="canDelete-' + escapeHTML(shareWith) + '">' + |
|
t('gallery', 'delete') + '</label>'; |
|
} |
|
html += '</div>'; |
|
html += '</li>'; |
|
html = $(html).appendTo('#dropdown #shareWithList'); |
|
if (oc_config.enable_avatars === true) { |
|
if (shareType === this.SHARE_TYPE_USER) { |
|
html.find('.avatar').avatar(escapeHTML(shareWith), 32); |
|
} else { |
|
//Add sharetype to generate different seed if there is a group and use with |
|
// the same name |
|
html.find('.avatar').imageplaceholder( |
|
escapeHTML(shareWith) + ' ' + shareType); |
|
} |
|
} |
|
// insert cruds button into last label element |
|
var lastLabel = html.find('>label:last'); |
|
if (lastLabel.exists()) { |
|
lastLabel.append(showCrudsButton); |
|
} |
|
else { |
|
html.find('.cruds').before(showCrudsButton); |
|
} |
|
if (!this.currentShares[shareType]) { |
|
this.currentShares[shareType] = []; |
|
} |
|
this.currentShares[shareType].push(shareItem); |
|
}, |
|
/** |
|
* Parses a string to an valid integer (unix timestamp) |
|
* @param time |
|
* @returns {*} |
|
* @internal Only used to work around a bug in the backend |
|
* @private |
|
*/ |
|
_parseTime: function (time) { |
|
if (_.isString(time)) { |
|
// skip empty strings and hex values |
|
if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) { |
|
return null; |
|
} |
|
time = parseInt(time, 10); |
|
if (isNaN(time)) { |
|
time = null; |
|
} |
|
} |
|
return time; |
|
} |
|
}; |
|
|
|
Gallery.Share = Share; |
|
})(jQuery, Gallery); |
|
|
|
$(document).ready(function () { |
|
|
|
if (typeof monthNames != 'undefined') { |
|
// min date should always be the next day |
|
var minDate = new Date(); |
|
minDate.setDate(minDate.getDate() + 1); |
|
$.datepicker.setDefaults({ |
|
monthNames: monthNames, |
|
monthNamesShort: $.map(monthNames, function (v) { |
|
return v.slice(0, 3) + '.'; |
|
}), |
|
dayNames: dayNames, |
|
dayNamesMin: $.map(dayNames, function (v) { |
|
return v.slice(0, 2); |
|
}), |
|
dayNamesShort: $.map(dayNames, function (v) { |
|
return v.slice(0, 3) + '.'; |
|
}), |
|
firstDay: firstDay, |
|
minDate: minDate |
|
}); |
|
} |
|
$(document).on('click', 'a.share', function (event) { |
|
event.stopPropagation(); |
|
if ($(this).data('item-type') !== undefined && $(this).data('path') !== undefined) { |
|
var itemType = $(this).data('item-type'); |
|
var path = $(this).data('path'); |
|
var appendTo = $(this).parents('#controls, #slideshow')[0]; |
|
var link = false; |
|
var possiblePermissions = $(this).data('possible-permissions'); |
|
if ($(this).data('link') !== undefined && $(this).data('link') == true) { |
|
link = true; |
|
} |
|
if (Gallery.Share.droppedDown) { |
|
if (path != $('#dropdown').data('path')) { |
|
Gallery.Share.hideDropDown(function () { |
|
Gallery.Share.showDropDown(itemType, path, appendTo, link, |
|
possiblePermissions); |
|
}); |
|
} else { |
|
Gallery.Share.hideDropDown(); |
|
} |
|
} else { |
|
Gallery.Share.showDropDown(itemType, path, appendTo, link, possiblePermissions); |
|
} |
|
} |
|
}); |
|
|
|
$(this).click(function (event) { |
|
var target = $(event.target); |
|
var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon') |
|
&& !target.closest('#ui-datepicker-div').length && |
|
!target.closest('.ui-autocomplete').length; |
|
if (Gallery.Share.droppedDown && isMatched && |
|
$('#dropdown').has(event.target).length === 0) { |
|
Gallery.Share.hideDropDown(); |
|
} |
|
}); |
|
|
|
$(document).on('click', '#dropdown .showCruds', function () { |
|
$(this).closest('li').find('.cruds').toggle(); |
|
return false; |
|
}); |
|
|
|
$(document).on('click', '#dropdown .unshare', function () { |
|
var $li = $(this).closest('li'); |
|
var shareType = $li.data('share-type'); |
|
var shareWith = $li.attr('data-share-with'); |
|
var shareId = $li.data('id'); |
|
var $button = $(this); |
|
|
|
if (!$button.is('a')) { |
|
$button = $button.closest('a'); |
|
} |
|
|
|
if ($button.hasClass('icon-loading-small')) { |
|
// deletion in progress |
|
return false; |
|
} |
|
$button.empty().addClass('icon-loading-small'); |
|
Gallery.Share.unshare(shareId, function () { |
|
$li.remove(); |
|
var index = Gallery.Share.itemShares[shareType].indexOf(shareWith); |
|
Gallery.Share.itemShares[shareType].splice(index, 1); |
|
// updated list of shares |
|
Gallery.Share.currentShares[shareType].splice(index, 1); |
|
}); |
|
|
|
return false; |
|
}); |
|
|
|
$(document).on('change', '#dropdown .permissions', function () { |
|
var $li = $(this).closest('li'); |
|
var checkboxes = $('.permissions', $li); |
|
if ($(this).attr('name') == 'edit') { |
|
var checked = $(this).is(':checked'); |
|
// Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck |
|
$(checkboxes).filter('input[name="create"]').attr('checked', checked); |
|
$(checkboxes).filter('input[name="update"]').attr('checked', checked); |
|
$(checkboxes).filter('input[name="delete"]').attr('checked', checked); |
|
} else { |
|
// Uncheck Edit if Create, Update, and Delete are not checked |
|
if (!$(this).is(':checked') |
|
&& !$(checkboxes).filter('input[name="create"]').is(':checked') |
|
&& !$(checkboxes).filter('input[name="update"]').is(':checked') |
|
&& !$(checkboxes).filter('input[name="delete"]').is(':checked')) { |
|
$(checkboxes).filter('input[name="edit"]').attr('checked', false); |
|
// Check Edit if Create, Update, or Delete is checked |
|
} else if (($(this).attr('name') == 'create' |
|
|| $(this).attr('name') == 'update' |
|
|| $(this).attr('name') == 'delete')) { |
|
$(checkboxes).filter('input[name="edit"]').attr('checked', true); |
|
} |
|
} |
|
var permissions = OC.PERMISSION_READ; |
|
$(checkboxes).filter(':not(input[name="edit"])').filter(':checked').each( |
|
function (index, checkbox) { |
|
permissions |= $(checkbox).data('permissions'); |
|
}); |
|
|
|
Gallery.Share.setPermissions($li.data('id'), permissions); |
|
}); |
|
|
|
$(document).on('change', '#dropdown #linkCheckbox', function () { |
|
var $dropDown = $('#dropdown'); |
|
var path = $dropDown.data('item-source'); |
|
var shareId = $('#linkCheckbox').data('id'); |
|
var shareWith = ''; |
|
var publicUpload = 0; |
|
var $loading = $dropDown.find('#link .icon-loading-small'); |
|
var $button = $(this); |
|
|
|
if (!$loading.hasClass('hidden')) { |
|
// already in progress |
|
return false; |
|
} |
|
|
|
if (this.checked) { |
|
// Reset password placeholder |
|
$('#linkPassText').attr('placeholder', |
|
t('gallery', 'Choose a password for the public link')); |
|
// Reset link |
|
$('#linkText').val(''); |
|
$('#showPassword').prop('checked', false); |
|
$('#linkPass').hide(); |
|
$('#linkSocial').hide(); |
|
$('#linkTextMore').hide(); |
|
$('#sharingDialogAllowPublicUpload').prop('checked', false); |
|
$('#expirationCheckbox').prop('checked', false); |
|
$('#expirationDate').hide(); |
|
var expireDateString = ''; |
|
// Create a link |
|
if (oc_appconfig.core.enforcePasswordForPublicLink === false) { |
|
expireDateString = Gallery.Share.getDefaultExpirationDate(); |
|
$loading.removeClass('hidden'); |
|
$button.addClass('hidden'); |
|
$button.prop('disabled', true); |
|
Gallery.Share.share( |
|
path, |
|
Gallery.Share.SHARE_TYPE_LINK, |
|
shareWith, |
|
publicUpload, |
|
null, |
|
OC.PERMISSION_READ, |
|
function (data) { |
|
$loading.addClass('hidden'); |
|
$button.removeClass('hidden'); |
|
$button.prop('disabled', false); |
|
Gallery.Share.showLink(data.id, data.token, null); |
|
}); |
|
} else { |
|
$('#linkPass').slideToggle(OC.menuSpeed); |
|
$('#linkPassText').focus(); |
|
} |
|
if (expireDateString !== '') { |
|
Gallery.Share.showExpirationDate(expireDateString); |
|
} |
|
} else { |
|
// Delete private link |
|
Gallery.Share.hideLink(); |
|
$('#expiration').slideUp(OC.menuSpeed); |
|
if ($('#linkText').val() !== '') { |
|
$loading.removeClass('hidden'); |
|
$button.addClass('hidden'); |
|
$button.prop('disabled', true); |
|
Gallery.Share.unshare(shareId, function () { |
|
$loading.addClass('hidden'); |
|
$button.removeClass('hidden'); |
|
$button.prop('disabled', false); |
|
$('#linkCheckbox').data('id', undefined); |
|
Gallery.Share.itemShares[Gallery.Share.SHARE_TYPE_LINK] = false; |
|
}); |
|
} |
|
} |
|
}); |
|
|
|
$(document).on('click', '#dropdown #linkText', function () { |
|
$(this).focus(); |
|
$(this).select(); |
|
}); |
|
|
|
// Handle the Allow Public Upload Checkbox |
|
$(document).on('click', '#sharingDialogAllowPublicUpload', function () { |
|
|
|
// Gather data |
|
var $dropDown = $('#dropdown'); |
|
var shareId = $('#linkCheckbox').data('id'); |
|
var allowPublicUpload = $(this).is(':checked'); |
|
var $button = $(this); |
|
var $loading = $dropDown.find('#allowPublicUploadWrapper .icon-loading-small'); |
|
|
|
if (!$loading.hasClass('hidden')) { |
|
// already in progress |
|
return false; |
|
} |
|
|
|
// Update the share information |
|
$button.addClass('hidden'); |
|
$button.prop('disabled', true); |
|
$loading.removeClass('hidden'); |
|
//(path, shareType, shareWith, publicUpload, password, permissions) |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
publicUpload: allowPublicUpload |
|
} |
|
}).done(function () { |
|
$loading.addClass('hidden'); |
|
$button.removeClass('hidden'); |
|
$button.prop('disabled', false); |
|
}); |
|
}); |
|
|
|
$(document).on('click', '#dropdown #showPassword', function () { |
|
$('#linkPass').slideToggle(OC.menuSpeed); |
|
if (!$('#showPassword').is(':checked')) { |
|
var shareId = $('#linkCheckbox').data('id'); |
|
var $loading = $('#showPassword .icon-loading-small'); |
|
|
|
$loading.removeClass('hidden'); |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
password: null |
|
} |
|
}).done(function () { |
|
$loading.addClass('hidden'); |
|
$('#linkPassText').attr('placeholder', |
|
t('gallery', 'Choose a password for the public link')); |
|
}); |
|
} else { |
|
$('#linkPassText').focus(); |
|
} |
|
}); |
|
|
|
$(document).on('focusout keyup', '#dropdown #linkPassText', function (event) { |
|
var linkPassText = $('#linkPassText'); |
|
if (linkPassText.val() != '' && (event.type == 'focusout' || event.keyCode == 13)) { |
|
var dropDown = $('#dropdown'); |
|
var $loading = dropDown.find('#linkPass .icon-loading-small'); |
|
var shareId = $('#linkCheckbox').data('id'); |
|
|
|
$loading.removeClass('hidden'); |
|
linkPassText.removeClass('warning'); |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
password: $('#linkPassText').val() |
|
} |
|
}).done(function (data) { |
|
$loading.addClass('hidden'); |
|
linkPassText.val(''); |
|
linkPassText.attr('placeholder', t('gallery', 'Password protected')); |
|
|
|
if (oc_appconfig.core.enforcePasswordForPublicLink) { |
|
Gallery.Share.showLink(data.id, data.token, "password set"); |
|
} |
|
}).fail(function (xhr) { |
|
var result = xhr.responseJSON; |
|
$loading.addClass('hidden'); |
|
linkPassText.val(''); |
|
linkPassText.addClass('warning'); |
|
linkPassText.attr('placeholder', result.ocs.meta.message); |
|
}); |
|
} |
|
}); |
|
|
|
$(document).on('click', '#dropdown #expirationCheckbox', function () { |
|
if (this.checked) { |
|
Gallery.Share.showExpirationDate(''); |
|
} else { |
|
var shareId = $('#linkCheckbox').data('id'); |
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
expireDate: '' |
|
} |
|
}).done(function () { |
|
$('#expirationDate').slideUp(OC.menuSpeed); |
|
if (oc_appconfig.core.defaultExpireDateEnforced === false) { |
|
$('#defaultExpireMessage').slideDown(OC.menuSpeed); |
|
} |
|
}).fail(function () { |
|
OC.dialogs.alert(t('gallery', 'Error unsetting expiration date'), |
|
t('gallery', 'Error')); |
|
}); |
|
} |
|
}); |
|
|
|
$(document).on('change', '#dropdown #expirationDate', function () { |
|
var shareId = $('#linkCheckbox').data('id'); |
|
|
|
$(this).tooltip('hide'); |
|
$(this).removeClass('error'); |
|
|
|
$.ajax({ |
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId + |
|
'?format=json', |
|
type: 'PUT', |
|
data: { |
|
expireDate: $(this).val() |
|
} |
|
}).done(function () { |
|
if (oc_appconfig.core.defaultExpireDateEnforced === 'no') { |
|
$('#defaultExpireMessage').slideUp(OC.menuSpeed); |
|
} |
|
}).fail(function (xhr) { |
|
var result = xhr.responseJSON; |
|
var expirationDateField = $('#dropdown #expirationDate'); |
|
if (result && !result.ocs.meta.message) { |
|
expirationDateField.attr('original-title', |
|
t('gallery', 'Error setting expiration date')); |
|
} else { |
|
expirationDateField.attr('original-title', result.ocs.meta.message); |
|
} |
|
expirationDateField.tooltip({placement: 'top'}); |
|
expirationDateField.tooltip('show'); |
|
expirationDateField.addClass('error'); |
|
}); |
|
}); |
|
|
|
|
|
$(document).on('submit', '#dropdown #emailPrivateLink', function (event) { |
|
event.preventDefault(); |
|
var link = $('#linkText').val(); |
|
var itemType = $('#dropdown').data('item-type'); |
|
var itemSource = $('#dropdown').data('item-source'); |
|
var fileName = $('.last').children()[0].innerText; |
|
var email = $('#email').val(); |
|
var expirationDate = ''; |
|
if ($('#expirationCheckbox').is(':checked') === true) { |
|
expirationDate = $("#expirationDate").val(); |
|
} |
|
if (email != '') { |
|
$('#email').prop('disabled', true); |
|
$('#email').val(t('gallery', 'Sending…')); |
|
$('#emailButton').prop('disabled', true); |
|
|
|
$.post(OC.filePath('core', 'ajax', 'share.php'), { |
|
action: 'email', |
|
toaddress: email, |
|
link: link, |
|
file: fileName, |
|
itemType: itemType, |
|
itemSource: itemSource, |
|
expiration: expirationDate |
|
}, |
|
function (result) { |
|
$('#email').prop('disabled', false); |
|
$('#emailButton').prop('disabled', false); |
|
if (result && result.status == 'success') { |
|
$('#email').css('font-weight', 'bold').val(t('gallery', 'Email sent')); |
|
setTimeout(function () { |
|
$('#email').css('font-weight', 'normal').val(''); |
|
}, 2000); |
|
} else { |
|
OC.dialogs.alert(result.data.message, t('gallery', 'Error while sharing')); |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
$(document).on('click', '#dropdown input[name=mailNotification]', function () { |
|
var $li = $(this).closest('li'); |
|
var itemType = $('#dropdown').data('item-type'); |
|
var itemSource = $('a.share').data('item-source'); |
|
var action = ''; |
|
if (this.checked) { |
|
action = 'informRecipients'; |
|
} else { |
|
action = 'informRecipientsDisabled'; |
|
} |
|
var shareType = $li.data('share-type'); |
|
var shareWith = $li.attr('data-share-with'); |
|
$.post(OC.filePath('core', 'ajax', 'share.php'), { |
|
action: action, |
|
recipient: shareWith, |
|
shareType: shareType, |
|
itemSource: itemSource, |
|
itemType: itemType |
|
}, function (result) { |
|
if (result.status !== 'success') { |
|
OC.dialogs.alert(t('gallery', result.data.message), t('gallery', 'Warning')); |
|
} |
|
}); |
|
}); |
|
|
|
$(document).on('click', '#dropdown .pop-up', function(event) { |
|
event.preventDefault(); |
|
event.stopPropagation(); |
|
|
|
var url = $(event.currentTarget).data('url'); |
|
var newWindow = $(event.currentTarget).data('window'); |
|
$(event.currentTarget).tooltip('hide'); |
|
if (url) { |
|
if (newWindow === true) { |
|
var width = 600; |
|
var height = 400; |
|
var left = (screen.width / 2) - (width / 2); |
|
var top = (screen.height / 2) - (height / 2); |
|
|
|
window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left); |
|
} else { |
|
window.location.href = url; |
|
} |
|
} |
|
}); |
|
|
|
$(document).on('click', '#dropdown .icon-more', function(event) { |
|
event.preventDefault(); |
|
event.stopPropagation(); |
|
|
|
var children = event.currentTarget.parentNode.children; |
|
|
|
$.each(children, function (key, value) { |
|
if (value.classList.contains('popovermenu')) { |
|
$(value).toggle(); |
|
} |
|
}); |
|
}); |
|
}); |
|
|
|
|
|
|