Server IP : 184.154.167.98 / Your IP : 18.223.210.83 Web Server : Apache System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : puertode ( 1767) PHP Version : 7.2.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/puertode/nextclouddatacont/appdata_ocpim8nlaclp/js/gallery/ |
Upload File : |
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) { //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(); } 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'); $.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.attr('placeholder', result.data.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(); } }); }); });