You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4570 lines
228 KiB

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, '&lt;');
}
/* Sanitize element content to be template-safe */
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
/* Get the element's text content */
content = currentNode.textContent;
content = content.replace(MUSTACHE_EXPR, ' ');
content = content.replace(ERB_EXPR, ' ');
if (currentNode.textContent !== content) {
DOMPurify.removed.push({element: currentNode.cloneNode()});
currentNode.textContent = content;
}
}
/* Execute a hook if present */
_executeHook('afterSanitizeElements', currentNode, null);
return false;
};
var DATA_ATTR = /^data-[\-\w.\u00B7-\uFFFF]/;
var ARIA_ATTR = /^aria-[\-\w]+$/;
var IS_ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
var IS_SCRIPT_OR_DATA = /^(?:\w+script|data):/i;
/* This needs to be extensive thanks to Webkit/Blink's behavior */
var ATTR_WHITESPACE = /[\x00-\x20\xA0\u1680\u180E\u2000-\u2029\u205f\u3000]/g;
/**
* _sanitizeAttributes
*
* @protect attributes
* @protect nodeName
* @protect removeAttribute
* @protect setAttribute
*
* @param node to sanitize
* @return void
*/
var _sanitizeAttributes = function(currentNode) {
var attr, name, value, lcName, idAttr, attributes, hookEvent, l;
/* Execute a hook if present */
_executeHook('beforeSanitizeAttributes', currentNode, null);
attributes = currentNode.attributes;
/* Check if we have attributes; if not we might have a text node */
if (!attributes) { return; }
hookEvent = {
attrName: '',
attrValue: '',
keepAttr: true,
allowedAttributes: ALLOWED_ATTR
};
l = attributes.length;
/* Go backwards over all attributes; safely remove bad ones */
while (l--) {
attr = attributes[l];
name = attr.name;
value = attr.value.trim();
lcName = name.toLowerCase();
/* Execute a hook if present */
hookEvent.attrName = lcName;
hookEvent.attrValue = value;
hookEvent.keepAttr = true;
_executeHook('uponSanitizeAttribute', currentNode, hookEvent );
value = hookEvent.attrValue;
/* Remove attribute */
// Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
// remove a "name" attribute from an <img> tag that has an "id"
// attribute at the time.
if (lcName === 'name' &&
currentNode.nodeName === 'IMG' && attributes.id) {
idAttr = attributes.id;
attributes = Array.prototype.slice.apply(attributes);
_removeAttribute('id', currentNode);
_removeAttribute(name, currentNode);
if (attributes.indexOf(idAttr) > l) {
currentNode.setAttribute('id', idAttr.value);
}
} else if (
// This works around a bug in Safari, where input[type=file]
// cannot be dynamically set after type has been removed
currentNode.nodeName === 'INPUT' && lcName === 'type' &&
value === 'file' && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
continue;
} else {
// This avoids a crash in Safari v9.0 with double-ids.
// The trick is to first set the id to be empty and then to
// remove the attriubute
if (name === 'id') {
currentNode.setAttribute(name, '');
}
_removeAttribute(name, currentNode);
}
/* Did the hooks approve of the attribute? */
if (!hookEvent.keepAttr) {
continue;
}
/* Make sure attribute cannot clobber */
if (SANITIZE_DOM &&
(lcName === 'id' || lcName === 'name') &&
(value in window || value in document || value in formElement)) {
continue;
}
/* Sanitize attribute content to be template-safe */
if (SAFE_FOR_TEMPLATES) {
value = value.replace(MUSTACHE_EXPR, ' ');
value = value.replace(ERB_EXPR, ' ');
}
/* Allow valid data-* attributes: At least one character after "-"
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
We don't need to check the value; it's always URI safe. */
if (ALLOW_DATA_ATTR && DATA_ATTR.test(lcName)) {
// This attribute is safe
}
else if (ALLOW_ARIA_ATTR && ARIA_ATTR.test(lcName)) {
// This attribute is safe
}
/* Otherwise, check the name is permitted */
else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
continue;
}
/* Check value is safe. First, is attr inert? If so, is safe */
else if (URI_SAFE_ATTRIBUTES[lcName]) {
// This attribute is safe
}
/* Check no script, data or unknown possibly unsafe URI
unless we know URI values are safe for that attribute */
else if (IS_ALLOWED_URI.test(value.replace(ATTR_WHITESPACE,''))) {
// This attribute is safe
}
/* Keep image data URIs alive if src/xlink:href is allowed */
else if (
(lcName === 'src' || lcName === 'xlink:href') &&
value.indexOf('data:') === 0 &&
DATA_URI_TAGS[currentNode.nodeName.toLowerCase()]) {
// This attribute is safe
}
/* Allow unknown protocols: This provides support for links that
are handled by protocol handlers which may be unknown ahead of
time, e.g. fb:, spotify: */
else if (
ALLOW_UNKNOWN_PROTOCOLS &&
!IS_SCRIPT_OR_DATA.test(value.replace(ATTR_WHITESPACE,''))) {
// This attribute is safe
}
/* Check for binary attributes */
else if (!value) {
// binary attributes are safe at this point
}
/* Anything else, presume unsafe, do not add it back */
else {
continue;
}
/* Handle invalid data-* attribute set by try-catching it */
try {
currentNode.setAttribute(name, value);
DOMPurify.removed.pop();
} catch (e) {}
}
/* Execute a hook if present */
_executeHook('afterSanitizeAttributes', currentNode, null);
};
/**
* _sanitizeShadowDOM
*
* @param fragment to iterate over recursively
* @return void
*/
var _sanitizeShadowDOM = function(fragment) {
var shadowNode;
var shadowIterator = _createIterator(fragment);
/* Execute a hook if present */
_executeHook('beforeSanitizeShadowDOM', fragment, null);
while ( (shadowNode = shadowIterator.nextNode()) ) {
/* Execute a hook if present */
_executeHook('uponSanitizeShadowNode', shadowNode, null);
/* Sanitize tags and elements */
if (_sanitizeElements(shadowNode)) {
continue;
}
/* Deep shadow DOM detected */
if (shadowNode.content instanceof DocumentFragment) {
_sanitizeShadowDOM(shadowNode.content);
}
/* Check attributes, sanitize if necessary */
_sanitizeAttributes(shadowNode);
}
/* Execute a hook if present */
_executeHook('afterSanitizeShadowDOM', fragment, null);
};
/**
* _executeHook
* Execute user configurable hooks
*
* @param {String} entryPoint Name of the hook's entry point
* @param {Node} currentNode
*/
var _executeHook = function(entryPoint, currentNode, data) {
if (!hooks[entryPoint]) { return; }
hooks[entryPoint].forEach(function(hook) {
hook.call(DOMPurify, currentNode, data, CONFIG);
});
};
/**
* sanitize
* Public method providing core sanitation functionality
*
* @param {String|Node} dirty string or DOM node
* @param {Object} configuration object
*/
DOMPurify.sanitize = function(dirty, cfg) {
var body, importedNode, currentNode, oldNode, nodeIterator, returnNode;
/* Make sure we have a string to sanitize.
DO NOT return early, as this will return the wrong type if
the user has requested a DOM object rather than a string */
if (!dirty) {
dirty = '<!-->';
}
/* Stringify, in case dirty is an object */
if (typeof dirty !== 'string' && !_isNode(dirty)) {
if (typeof dirty.toString !== 'function') {
throw new TypeError('toString is not a function');
} else {
dirty = dirty.toString();
}
}
/* Check we can run. Otherwise fall back or ignore */
if (!DOMPurify.isSupported) {
if (typeof window.toStaticHTML === 'object'
|| typeof window.toStaticHTML === 'function') {
if (typeof dirty === 'string') {
return window.toStaticHTML(dirty);
} else if (_isNode(dirty)) {
return window.toStaticHTML(dirty.outerHTML);
}
}
return dirty;
}
/* Assign config vars */
_parseConfig(cfg);
/* Clean up removed elements */
DOMPurify.removed = [];
if (dirty instanceof Node) {
/* If dirty is a DOM element, append to an empty document to avoid
elements being stripped by the parser */
body = _initDocument('<!-->');
importedNode = body.ownerDocument.importNode(dirty, true);
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
/* Node is already a body, use as is */
body = importedNode;
} else {
body.appendChild( importedNode );
}
} else {
/* Exit directly if we have nothing to do */
if (!RETURN_DOM && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
return dirty;
}
/* Initialize the document to work on */
body = _initDocument(dirty);
/* Check we have a DOM node from the data */
if (!body) {
return RETURN_DOM ? null : '';
}
}
/* Remove first element node (ours) if FORCE_BODY is set */
if (FORCE_BODY) {
_forceRemove(body.firstChild);
}
/* Get node iterator */
nodeIterator = _createIterator(body);
/* Now start iterating over the created document */
while ( (currentNode = nodeIterator.nextNode()) ) {
/* Fix IE's strange behavior with manipulated textNodes #89 */
if (currentNode.nodeType === 3 && currentNode === oldNode) {
continue;
}
/* Sanitize tags and elements */
if (_sanitizeElements(currentNode)) {
continue;
}
/* Shadow DOM detected, sanitize it */
if (currentNode.content instanceof DocumentFragment) {
_sanitizeShadowDOM(currentNode.content);
}
/* Check attributes, sanitize if necessary */
_sanitizeAttributes(currentNode);
oldNode = currentNode;
}
/* Return sanitized string or DOM */
if (RETURN_DOM) {
if (RETURN_DOM_FRAGMENT) {
returnNode = createDocumentFragment.call(body.ownerDocument);
while (body.firstChild) {
returnNode.appendChild(body.firstChild);
}
} else {
returnNode = body;
}
if (RETURN_DOM_IMPORT) {
/* adoptNode() is not used because internal state is not reset
(e.g. the past names map of a HTMLFormElement), this is safe
in theory but we would rather not risk another attack vector.
The state that is cloned by importNode() is explicitly defined
by the specs. */
returnNode = importNode.call(originalDocument, returnNode, true);
}
return returnNode;
}
return WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
};
/**
* addHook
* Public method to add DOMPurify hooks
*
* @param {String} entryPoint
* @param {Function} hookFunction
*/
DOMPurify.addHook = function(entryPoint, hookFunction) {
if (typeof hookFunction !== 'function') { return; }
hooks[entryPoint] = hooks[entryPoint] || [];
hooks[entryPoint].push(hookFunction);
};
/**
* removeHook
* Public method to remove a DOMPurify hook at a given entryPoint
* (pops it from the stack of hooks if more are present)
*
* @param {String} entryPoint
* @return void
*/
DOMPurify.removeHook = function(entryPoint) {
if (hooks[entryPoint]) {
hooks[entryPoint].pop();
}
};
/**
* removeHooks
* Public method to remove all DOMPurify hooks at a given entryPoint
*
* @param {String} entryPoint
* @return void
*/
DOMPurify.removeHooks = function(entryPoint) {
if (hooks[entryPoint]) {
hooks[entryPoint] = [];
}
};
/**
* removeAllHooks
* Public method to remove all DOMPurify hooks
*
* @return void
*/
DOMPurify.removeAllHooks = function() {
hooks = {};
};
return DOMPurify;
}));
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global DOMPurify, oc_requesttoken, Gallery */
// The Utility class can also be loaded by the Files app
window.Gallery = window.Gallery || {};
(function ($, OC, t, oc_requesttoken, Gallery) {
"use strict";
/**
* Contains utility methods
*
* @fixme OC.generateUrl, OC.buildQueryString, OC.Notification are private APIs
*
* @constructor
*/
var Utility = function () {
};
Utility.prototype = {
/**
* Detects if the browser is a recent or an old version of Internet Explorer
*
* @returns {string|boolean}
*/
getIeVersion: function () {
// Blocking IE8
if ($('html').is('.ie8')) {
return 'unsupportedIe';
} else if (navigator.userAgent.indexOf("MSIE") > 0) {
return 'unsupportedIe';
} else if (!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)) {
return 'modernIe';
} else if (navigator.userAgent.indexOf("Edge/") > 0) {
return 'edge';
}
return false;
},
/**
* Shows a notification to IE users, letting them know that they should use another browser
* in order to get the best experience
*
* @param {string} version
*/
showIeWarning: function (version) {
var line1 = t('gallery', 'This application may not work properly on your browser.');
var line2 = t('gallery',
'For an improved experience, please install one of the following alternatives');
var timeout = 15;
if (version === 'unsupportedIe') {
line1 = t('gallery', 'Your browser is not supported!');
line2 = t('gallery', 'please install one of the following alternatives');
timeout = 60;
}
var recommendedBrowsers = '</br>' +
'<a href="http://www.getfirefox.com"><strong>Mozilla Firefox</strong></a> or ' +
'<a href="https://www.google.com/chrome/"><strong>Google Chrome</strong></a>' +
'</br>';
var text = '<strong>' + line1 + '</strong></br>' + line2 + recommendedBrowsers;
this.showHtmlNotification(text, timeout);
},
/**
* Shows a notification at the top of the screen
*
* @param {string} text
* @param {int} timeout
*/
showHtmlNotification: function (text, timeout) {
var options = {
timeout: timeout,
isHTML: true
};
OC.Notification.showTemporary(text, options);
},
/**
* Returns the token allowing access to files shared via link
*
* @returns {string}
*/
getPublicToken: function () {
var element = $('#gallery');
var token;
if (element.data('token')) {
token = element.data('token');
}
if (element.data('requesttoken')) {
/* jshint camelcase: false */
oc_requesttoken = element.data('requesttoken');
}
return token;
},
/**
* Returns the host we can use for WebDAV
*
* On public galleries, we need to provide the token as authorization
*
* @returns {string}
*/
getWebdavHost: function () {
var host = OC.getHost();
if (Gallery.token) {
host = Gallery.token + '@' + host;
}
return host;
},
/**
* Returns the WebDAV endpoint we can use for files operations
*
* @returns {string}
*/
getWebdavRoot: function () {
var root = OC.linkToRemoteBase('webdav');
if (Gallery.token) {
root = root.replace('remote.php', 'public.php');
}
return root;
},
/**
* Builds the URL which will retrieve a large preview of the file
*
* @fixme we cannot get rid of oc_requesttoken parameter as it's missing from the headers
*
* @param {number} fileId
* @param {number} etag
*
* @return {string}
*/
getPreviewUrl: function (fileId, etag) {
var width = Math.ceil(screen.width * window.devicePixelRatio);
var height = Math.ceil(screen.height * window.devicePixelRatio);
/* Find value of longest edge. */
var longEdge = Math.max(width, height);
/* Find the next larger image size. */
if (longEdge % 100 !== 0) {
longEdge = ( longEdge + 100 ) - ( longEdge % 100 );
}
/* jshint camelcase: false */
var params = {
width: longEdge,
height: longEdge,
};
return this.buildGalleryUrl('preview', '/' + fileId, params);
},
/**
* Builds a URL pointing to one of the app's controllers
*
* @param {string} endPoint
* @param {undefined|string} path
* @param params
*
* @returns {string}
*/
buildGalleryUrl: function (endPoint, path, params) {
if (path === undefined) {
path = '';
}
var extension = '';
if (Gallery.token) {
params.token = Gallery.token;
extension = '.public';
}
var query = OC.buildQueryString(params);
return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension + path,
null) +
'?' +
query;
},
/**
* Builds a URL pointing to one of the files' controllers
*
* @param {string} path
* @param {string} files
*
* @returns {string}
*/
buildFilesUrl: function (path, files) {
var subUrl = '';
var params = {
path: path,
files: files
};
if (Gallery.token) {
params.token = Gallery.token;
subUrl = 's/{token}/download?dir={path}&files={files}';
} else {
subUrl = 'apps/files/ajax/download.php?dir={path}&files={files}';
}
return OC.generateUrl(subUrl, params);
},
/**
* Sorts arrays based on name or date
*
* @param {string} sortType
* @param {string} sortOrder
*
* @returns {Function}
*/
sortBy: function (sortType, sortOrder) {
if (sortType === 'name') {
if (sortOrder === 'asc') {
//sortByNameAsc
return function (a, b) {
return OC.Util.naturalSortCompare(a.path, b.path);
};
}
//sortByNameDes
return function (a, b) {
return -OC.Util.naturalSortCompare(a.path, b.path);
};
}
if (sortType === 'date') {
if (sortOrder === 'asc') {
//sortByDateAsc
return function (a, b) {
return b.mTime - a.mTime;
};
}
//sortByDateDes
return function (a, b) {
return a.mTime - b.mTime;
};
}
},
/**
* Adds hooks to DOMPurify
*/
addDomPurifyHooks: function () {
// allowed URI schemes
var whitelist = ['http', 'https'];
// build fitting regex
var regex = new RegExp('^(' + whitelist.join('|') + '):', 'gim');
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// This hook enforces URI scheme whitelist
// @link
// https://github.com/cure53/DOMPurify/blob/master/demos/hooks-scheme-whitelist.html
// build an anchor to map URLs to
var anchor = document.createElement('a');
// check all href attributes for validity
if (node.hasAttribute('href')) {
anchor.href = node.getAttribute('href');
if (anchor.protocol && !anchor.protocol.match(regex)) {
node.removeAttribute('href');
}
}
// check all action attributes for validity
if (node.hasAttribute('action')) {
anchor.href = node.getAttribute('action');
if (anchor.protocol && !anchor.protocol.match(regex)) {
node.removeAttribute('action');
}
}
// check all xlink:href attributes for validity
if (node.hasAttribute('xlink:href')) {
anchor.href = node.getAttribute('xlink:href');
if (anchor.protocol && !anchor.protocol.match(regex)) {
node.removeAttribute('xlink:href');
}
}
// This hook restores the proper, standard namespace in SVG files
var encodedXmlns, decodedXmlns;
// Restores namespaces which were put in the DOCTYPE by Illustrator
if (node.hasAttribute('xmlns') && node.getAttribute('xmlns') === '&ns_svg;') {
encodedXmlns = node.getAttribute('xmlns');
decodedXmlns = encodedXmlns.replace('&ns_svg;', 'http://www.w3.org/2000/svg');
node.setAttribute('xmlns', decodedXmlns);
}
if (node.hasAttribute('xmlns:xlink') &&
node.getAttribute('xmlns:xlink') === '&ns_xlink;') {
encodedXmlns = node.getAttribute('xmlns:xlink');
decodedXmlns =
encodedXmlns.replace('&ns_xlink;', 'http://www.w3.org/1999/xlink');
node.setAttribute('xmlns:xlink', decodedXmlns);
}
});
}
};
Gallery.Utility = Utility;
})(jQuery, OC, t, oc_requesttoken, Gallery);
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global oc_requesttoken, FileList, Gallery, SlideShow */
(function ($, OC, OCA, oc_requesttoken) {
"use strict";
var galleryFileAction = {
features: [],
mediaTypes: {},
scrollContainer: null,
slideShow: null,
/**
* Builds a URL pointing to one of the app's controllers
*
* @param {string} endPoint
* @param {undefined|string} path
* @param {Object} params
*
* @returns {string}
*/
buildGalleryUrl: function (endPoint, path, params) {
var extension = '';
var tokenElement = $('#sharingToken');
var token = (tokenElement.val()) ? tokenElement.val() : false;
if (token) {
params.token = token;
extension = '.public';
}
var query = OC.buildQueryString(params);
return OC.generateUrl('apps/gallery/' + endPoint + extension + path, null) + '?' +
query;
},
/**
* Registers a file action for each media type
*
* @param {Array} mediaTypes
*/
register: function (mediaTypes) {
if (OCA.Viewer) {
return;
}
//console.log("enabledPreviewProviders: ", mediaTypes);
if (mediaTypes) {
// Remove SVG if the user is using an insecure browser (IE8-9)
if (window.galleryFileAction.features.indexOf('native_svg') > -1 && !window.btoa) {
mediaTypes.splice(mediaTypes.indexOf('image/svg+xml'), 1);
}
galleryFileAction.mediaTypes = mediaTypes;
}
var i, mediaTypesLength = mediaTypes.length;
// We only want to create slideshows for supported media types
for (i = 0; i < mediaTypesLength; i++) {
// Each click handler gets the same function and images array and
// is responsible to load the slideshow
OCA.Files.fileActions.register(mediaTypes[i], 'View', OC.PERMISSION_READ, '',
galleryFileAction.onView);
OCA.Files.fileActions.setDefault(mediaTypes[i], 'View');
}
},
/**
* Prepares the features array
*
* This is duplicated from a method found in galleryconfig. It's done that way in order to
* avoid having to load the whole utility class in the Files app
*
* @param configFeatures
* @returns {Array}
*/
buildFeaturesList: function (configFeatures) {
var features = [];
var i, configFeaturesLength = configFeatures.length;
if (configFeaturesLength) {
for (i = 0; i < configFeaturesLength; i++) {
features.push(configFeatures[i]);
}
}
window.galleryFileAction.features = features;
},
/**
* Builds an array containing all the images we can show in the slideshow
*
* @param {string} filename
* @param {Object} context
*/
onView: function (filename, context) {
var imageUrl, downloadUrl;
var fileList = context.fileList;
var files = fileList.files;
var start = 0;
var images = [];
var dir = context.dir + '/';
var width = Math.ceil(screen.width * window.devicePixelRatio);
var height = Math.ceil(screen.height * window.devicePixelRatio);
/* Find value of longest edge. */
var longEdge = Math.max(width, height);
/* Find the next larger image size. */
if (longEdge % 100 !== 0) {
longEdge = ( longEdge + 100 ) - ( longEdge % 100 );
}
for (var i = 0; i < files.length; i++) {
var file = files[i];
// We only add images to the slideshow if we think we'll be able
// to generate previews for this media type
if (galleryFileAction.mediaTypes.indexOf(file.mimetype) > -1) {
/* jshint camelcase: false */
var params = {
width: longEdge,
height: longEdge,
c: file.etag,
requesttoken: oc_requesttoken
};
imageUrl = galleryFileAction.buildGalleryUrl('preview', '/' + file.id, params);
params = {
c: file.etag,
requesttoken: oc_requesttoken
};
downloadUrl =
galleryFileAction.buildGalleryUrl('files', '/download/' + file.id, params);
images.push({
name: file.name,
path: dir + file.name,
fileId: file.id,
mimeType: file.mimetype,
permissions: file.permissions,
url: imageUrl,
downloadUrl: downloadUrl
});
}
}
for (i = 0; i < images.length; i++) {
//console.log("Images in the slideshow : ", images[i]);
if (images[i].name === filename) {
start = i;
}
}
if ($.isEmptyObject(galleryFileAction.slideShow)) {
galleryFileAction.slideShow = new SlideShow();
$.when(galleryFileAction.slideShow.init(
false,
null,
window.galleryFileAction.features
)).then(function () {
// Don't show the download button on the "Files" slideshow
galleryFileAction._startSlideshow(images, start);
});
} else {
galleryFileAction._startSlideshow(images, start);
}
},
/**
* Launches the slideshow
*
* @param {{name:string, url: string, path: string, fallBack: string}[]} images
* @param {number} start
* @private
*/
_startSlideshow: function (images, start) {
galleryFileAction.slideShow.setImages(images, false);
var scrollTop = galleryFileAction.scrollContainer.scrollTop();
// This is only called when the slideshow is stopped
galleryFileAction.slideShow.onStop = function () {
FileList.$fileList.one('updated', function () {
galleryFileAction.scrollContainer.scrollTop(scrollTop);
});
};
// Only modern browsers can manipulate history
if (history && history.replaceState) {
// This stores the fileslist in the history state
var stateData = {
dir: FileList.getCurrentDirectory()
};
history.replaceState(stateData, document.title, window.location);
// This creates a new entry in history for the slideshow. It will
// be updated as the user navigates from picture to picture
history.pushState(null, '', '#loading');
}
galleryFileAction.slideShow.show(start);
}
};
window.galleryFileAction = galleryFileAction;
})(jQuery, OC, OCA, oc_requesttoken);
$(document).ready(function () {
"use strict";
// Deactivates fileaction on public preview page
if ($('#imgframe').length > 0) {
return true;
}
if ($('html').is('.ie8')) {
return true; //deactivate in IE8
}
window.galleryFileAction.scrollContainer = $('#app-content');
if ($('#isPublic').val()) {
window.galleryFileAction.scrollContainer = $(window);
}
var utility = new Gallery.Utility();
utility.addDomPurifyHooks();
// Retrieve the config as well as the list of supported media types.
// The list of media files is retrieved when the user clicks on a row
var url = window.galleryFileAction.buildGalleryUrl('config', '', {extramediatypes: 1});
$.getJSON(url).then(function (config) {
window.galleryFileAction.buildFeaturesList(config.features);
window.galleryFileAction.register(config.mediatypes);
});
// create public share links as from the files app
if (!Gallery.appName) {
Gallery.appName = 'files';
}
});
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global Gallery, Thumbnails, DOMPurify */
(function ($, OC, OCA, t) {
"use strict";
/**
* Slideshow featuring zooming
*
* @constructor
*/
var SlideShow = function () {
};
SlideShow.prototype = {
slideshowTemplate: null,
container: null,
zoomablePreviewContainer: null,
controls: null,
imageCache: {},
/** {Image} */
currentImage: null,
errorLoadingImage: false,
onStop: null,
zoomablePreview: null,
active: false,
backgroundToggle: false,
// We need 6 hexas for comparison reasons
darkBackgroundColour: '#000000',
lightBackgroundColour: '#ffffff',
/**
* Initialises the slideshow
*
* @param {boolean} autoPlay
* @param {number} interval
* @param {Array} features
*/
init: function (autoPlay, interval, features) {
if (features.indexOf('background_colour_toggle') > -1) {
this.backgroundToggle = true;
}
return $.when(this._getSlideshowTemplate()).then(function ($tmpl) {
// Move the slideshow outside the content so we can hide the content
$('body').append($tmpl);
this.container = $('#slideshow');
this.zoomablePreviewContainer = this.container.find('.bigshotContainer');
this.zoomablePreview = new SlideShow.ZoomablePreview(this.container);
this.controls =
new SlideShow.Controls(
this,
this.container,
this.zoomablePreview,
interval,
features,
this.restoreContent.bind(this));
this.controls.init();
this._initControlsAutoFader();
// Only modern browsers can manipulate history
if (history && history.pushState) {
// Stop the slideshow when backing out.
$(window).bind('popstate.slideshow', function () {
if (this.active === true) {
this.active = false;
this.controls.stop();
}
}.bind(this));
}
}.bind(this)).fail(function () {
OC.Notification.show(t('gallery', 'Error loading slideshow template'));
});
},
/**
* Refreshes the slideshow's data
*
* @param {{name:string, url: string, path: string, fallBack: string}[]} images
* @param {boolean} autoPlay
*/
setImages: function (images, autoPlay) {
this._hideImage();
this.images = images;
this.controls.update(images, autoPlay);
},
/**
* Hides the content behind the slideshow
*
* This should be called when the slideshow is shown.
*
* It hides the content (and, in the public share page, also the footer)
* to ensure that the body size is just the slideshow size and thus no
* scroll bars are shown.
*/
hideContent: function () {
this._savedScrollPosition = $(window).scrollTop();
$('#content').hide();
$('footer').hide();
},
/**
* Shows again the content behind the slideshow
*
* This should be called when the slideshow is hidden.
*
* It restores the content hidden when calling "hideContent", including
* the vertical scrolling position.
*/
restoreContent: function () {
$('#content').show();
$('footer').show();
if (this._savedScrollPosition) {
$(window).scrollTop(this._savedScrollPosition);
}
},
/**
* Launches the slideshow
*
* @param {number} index
*
* @returns {*}
*/
show: function (index) {
this.hideErrorNotification();
this.active = true;
this.container.show();
this.hideContent();
this.container.css('background-position', 'center');
this._hideImage();
this.container.find('.icon-loading-dark').show();
var currentImageId = index;
return this.loadImage(this.images[index]).then(function (img) {
this.container.css('background-position', '-10000px 0');
// check if we moved along while we were loading
if (currentImageId === index) {
var image = this.images[index];
var transparent = this._isTransparent(image.mimeType);
this.controls.showActionButtons(transparent, Gallery.token, image.permissions);
this.errorLoadingImage = false;
this.currentImage = img;
img.setAttribute('alt', image.name);
$(img).css('position', 'absolute');
$(img).css('background-color', image.backgroundColour);
if (transparent && this.backgroundToggle === true) {
var $border = 30 / window.devicePixelRatio;
$(img).css('outline', $border + 'px solid ' + image.backgroundColour);
}
this.zoomablePreview.startBigshot(img, this.currentImage, image.mimeType);
this._setUrl(image.path);
this.controls.show(currentImageId);
this.container.find('.icon-loading-dark').hide();
}
}.bind(this), function () {
// Don't do anything if the user has moved along while we were loading as it would
// mess up the index
if (currentImageId === index) {
this.errorLoadingImage = true;
this.showErrorNotification(null);
this._setUrl(this.images[index].path);
this.images.splice(index, 1);
this.controls.updateControls(this.images, this.errorLoadingImage);
}
}.bind(this));
},
/**
* Loads the image to show in the slideshow and preloads the next one
*
* @param {Object} preview
*
* @returns {*}
*/
loadImage: function (preview) {
var url = preview.url;
var mimeType = preview.mimeType;
if (!this.imageCache[url]) {
this.imageCache[url] = new $.Deferred();
var image = new Image();
image.onload = function () {
preview.backgroundColour = this._getBackgroundColour(image, mimeType);
if (this.imageCache[url]) {
this.imageCache[url].resolve(image);
}
}.bind(this);
image.onerror = function () {
if (this.imageCache[url]) {
this.imageCache[url].reject(url);
}
}.bind(this);
if (mimeType === 'image/svg+xml') {
image.src = this._getSVG(url);
} else {
image.src = url;
}
}
return this.imageCache[url];
},
/**
* Shows a new image in the slideshow and preloads the next in the list
*
* @param {number} current
* @param {Object} next
*/
next: function (current, next) {
this.show(current).then(function () {
// Preloads the next image in the list
this.loadImage(next);
}.bind(this));
},
/**
* Determines which colour to use for the background
*
* @param {*} image
* @param {string} mimeType
*
* @returns {string}
* @private
*/
_getBackgroundColour: function (image, mimeType) {
var backgroundColour = this.darkBackgroundColour;
if (this._isTransparent(mimeType) && this._isMainlyDark(image)) {
backgroundColour = this.lightBackgroundColour;
}
return backgroundColour;
},
/**
* Calculates the luminance of an image to determine if an image is mainly dark
*
* @param {*} image
*
* @returns {boolean}
* @private
*/
_isMainlyDark: function (image) {
var isMainlyDark = false;
var numberOfSamples = 1000; // Seems to be the sweet spot
// The name has to be 'canvas'
var lumiCanvas = document.createElement('canvas');
var imgArea = image.width * image.height;
var canArea = numberOfSamples;
var factor = Math.sqrt(canArea / imgArea);
var scaledWidth = factor * image.width;
var scaledHeight = factor * image.height;
lumiCanvas.width = scaledWidth;
lumiCanvas.height = scaledHeight;
var lumiCtx = lumiCanvas.getContext('2d');
lumiCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
var imgData = lumiCtx.getImageData(0, 0, lumiCanvas.width, lumiCanvas.height);
var pix = imgData.data; // pix.length will be approximately 4*numberOfSamples (for RGBA)
var pixelArraySize = pix.length;
var totalLuminance = 0;
var sampleNumber = 1;
var averageLuminance;
var totalAlpha = 0;
var alphaLevel;
var red = 0;
var green = 0;
var blue = 0;
var alpha = 0;
var lum = 0;
var alphaThreshold = 0.1;
var sampleCounter = 0;
var itemsPerPixel = 4; // red, green, blue, alpha
// i += 4 because 4 colours for every pixel
for (var i = 0, n = pixelArraySize; i < n; i += itemsPerPixel) {
sampleCounter++;
alpha = pix[i + 3] / 255;
totalAlpha += alpha;
if (Math.ceil(alpha * 100) / 100 > alphaThreshold) {
red = pix[i];
green = pix[i + 1];
blue = pix[i + 2];
// Luminance formula from
// http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
lum = (red + red + green + green + green + blue) / 6;
//lum = (red * 0.299 + green * 0.587 + blue * 0.114 );
totalLuminance += lum * alpha;
sampleNumber++;
}
}
// Deletes the canvas
lumiCanvas = null;
// Calculate the optimum background colour for this image
averageLuminance = Math.ceil((totalLuminance / sampleNumber) * 100) / 100;
alphaLevel = Math.ceil((totalAlpha / numberOfSamples) * 100);
if (averageLuminance < 60 && alphaLevel < 90) {
isMainlyDark = true;
}
return isMainlyDark;
},
/**
* Stops the slideshow
*/
stop: function () {
this.active = false;
this.images = null;
this._hideImage();
if (this.onStop) {
this.onStop();
}
},
/**
* Sends the current image as a download
*
* @param {string} downloadUrl
*
* @returns {boolean}
*/
getImageDownload: function (downloadUrl) {
OC.redirect(downloadUrl);
return false;
},
/**
* Changes the colour of the background of the image
*/
toggleBackground: function () {
var toHex = function (x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
};
var container = this.zoomablePreviewContainer.children('img');
var rgb = container.css('background-color').match(/\d+/g);
var hex = "#" + toHex(rgb[0]) + toHex(rgb[1]) + toHex(rgb[2]);
var $border = 30 / window.devicePixelRatio;
var newBackgroundColor;
// Grey #363636
if (hex === this.darkBackgroundColour) {
newBackgroundColor = this.lightBackgroundColour;
} else {
newBackgroundColor = this.darkBackgroundColour;
}
container.css('background-color', newBackgroundColor);
if (this.backgroundToggle === true) {
container.css('outline', $border + 'px solid ' + newBackgroundColor);
}
},
/**
* Shows an error notification
*
* @param {string} message
*/
showErrorNotification: function (message) {
if ($.isEmptyObject(message)) {
message = t('gallery',
'<strong>Error!</strong> Could not generate a preview of this file.<br>' +
'Please go to the next slide while we remove this image from the slideshow');
}
this.container.find('.notification').html(message);
this.container.find('.notification').show();
this.controls.hideButton('.changeBackground');
},
/**
* Hides the error notification
*/
hideErrorNotification: function () {
this.container.find('.notification').hide();
this.container.find('.notification').html('');
},
/**
* Removes a specific button from the interface
*
* @param button
*/
removeButton: function (button) {
this.controls.removeButton(button);
},
/**
* Deletes an image from the slideshow
*
* @param {object} image
* @param {number} currentIndex
*/
deleteImage: function (image, currentIndex) {
// These are Gallery specific commands to be replaced
// which should sit somewhere else
if (!window.galleryFileAction) {
delete Gallery.imageMap[image.path];
delete Thumbnails.map[image.file];
Gallery.albumMap[Gallery.currentAlbum].images.splice(currentIndex, 1);
Gallery.view.init(Gallery.currentAlbum);
}
},
/**
* Automatically fades the controls after 3 seconds
*
* @private
*/
_initControlsAutoFader: function () {
var inactiveCallback = function () {
this.container.addClass('inactive');
}.bind(this);
var inactiveTimeout = setTimeout(inactiveCallback, 3000);
this.container.on('mousemove touchstart', function () {
this.container.removeClass('inactive');
clearTimeout(inactiveTimeout);
inactiveTimeout = setTimeout(inactiveCallback, 3000);
}.bind(this));
},
/**
* Simplest way to detect if image is transparent.
*
* That's very inaccurate since it doesn't include images which support transparency
*
* @param mimeType
* @returns {boolean}
* @private
*/
_isTransparent: function (mimeType) {
return !(mimeType === 'image/jpeg'
|| mimeType === 'image/x-dcraw'
|| mimeType === 'application/font-sfnt'
|| mimeType === 'application/x-font'
);
},
/**
* Changes the browser Url, based on the current image
*
* @param {string} path
* @private
*/
_setUrl: function (path) {
if (history && history.replaceState) {
history.replaceState('', '', '#' + encodeURI(path));
}
},
/**
* Hides the current image (before loading the next)
*
* @private
*/
_hideImage: function () {
this.zoomablePreviewContainer.empty();
this.controls.hideActionButtons();
},
/**
* Retrieves an SVG
*
* An SVG can't be simply attached to a src attribute like a bitmap image
*
* @param {string} source
*
* @returns {*}
* @private
*/
_getSVG: function (source) {
var svgPreview = null;
// DOMPurify only works with IE10+ and we load SVGs in the IMG tag
if (window.btoa &&
document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image",
"1.1")) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", source, false);
xmlHttp.send(null);
if (xmlHttp.status === 200) {
var pureSvg = DOMPurify.sanitize(xmlHttp.responseText, {ADD_TAGS: ['filter']});
// Remove XML comment garbage left in the purified data
var badTag = pureSvg.indexOf(']&gt;');
var fixedPureSvg = pureSvg.substring(badTag < 0 ? 0 : 5, pureSvg.length);
svgPreview = "data:image/svg+xml;base64," + window.btoa(fixedPureSvg);
}
}
return svgPreview;
},
/**
* Retrieves the slideshow's template
*
* @returns {*}
* @private
*/
_getSlideshowTemplate: function () {
var defer = $.Deferred();
if (!this.$slideshowTemplate) {
var self = this;
var url = OC.generateUrl('apps/gallery/slideshow', null);
$.get(url, function (tmpl) {
var template = $(tmpl);
var tmplButton;
var buttonsArray = [
{
el: '.next',
trans: t('gallery', 'Next')
},
{
el: '.play',
trans: t('gallery', 'Play'),
toolTip: true
},
{
el: '.pause',
trans: t('gallery', 'Pause'),
toolTip: true
},
{
el: '.previous',
trans: t('gallery', 'Previous')
},
{
el: '.exit',
trans: t('gallery', 'Close'),
toolTip: true
},
{
el: '.downloadImage',
trans: t('gallery', 'Download'),
toolTip: true
},
{
el: '.changeBackground',
trans: t('gallery', 'Toggle background'),
toolTip: true
},
{
el: '.deleteImage',
trans: t('gallery', 'Delete'),
toolTip: true
},
{
el: '.shareImage',
trans: t('gallery', 'Share'),
toolTip: true
}
];
for (var i = 0; i < buttonsArray.length; i++) {
var button = buttonsArray[i];
tmplButton = template.find(button.el);
tmplButton.val(button.trans);
if (button.toolTip) {
tmplButton.attr("title", button.trans);
}
}
self.$slideshowTemplate = template;
defer.resolve(self.$slideshowTemplate);
})
.fail(function () {
defer.reject();
});
} else {
defer.resolve(this.$slideshowTemplate);
}
return defer.promise();
}
};
window.SlideShow = SlideShow;
})(jQuery, OC, OCA, t);
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global OC, SlideShow */
(function ($, SlideShow) {
"use strict";
/**
* Button and key controls for the slideshow
*
* @param {Object} slideshow
* @param {*} container
* @param {Object} zoomablePreview
* @param {number} interval
* @param {Array} features
* @param {Function} restoreContentCallback
* @constructor
*/
var Controls = function (slideshow, container, zoomablePreview, interval, features, restoreContentCallback) {
this.slideshow = slideshow;
this.container = container;
this.zoomablePreview = zoomablePreview;
this.progressBar = container.find('.progress');
this.interval = interval || 5000;
if (features.indexOf('background_colour_toggle') > -1) {
this.backgroundToggle = true;
}
this.restoreContentCallback = restoreContentCallback;
};
Controls.prototype = {
current: 0,
errorLoadingImage: false,
playTimeout: 0,
playing: false,
active: false,
backgroundToggle: false,
/**
* Initialises the controls
*/
init: function () {
var makeCallBack = function (handler) {
return function (evt) {
if (!this.active) {
return;
}
evt.stopPropagation();
evt.preventDefault();
handler.call(this);
}.bind(this);
}.bind(this);
this._buttonSetup(makeCallBack);
this._specialButtonSetup(makeCallBack);
this._keyCodeSetup(makeCallBack);
},
/**
* Updates the controls
*
* @param {{name:string, url: string, path: string, fallBack: string}[]} images
* @param {boolean} autoPlay
*/
update: function (images, autoPlay) {
this.images = images;
this.active = true;
this.showButton('.play');
this.hideButton('.pause, .progress');
this.playing = false;
// Hide prev/next and play buttons when we only have one pic
this.container.find('.next, .previous, .play').toggle(this.images.length > 1);
// Hide the action buttons until we have something to show
this.hideActionButtons();
if (autoPlay) {
this._playPauseToggle();
}
},
/**
* Initialises local variables when the show starts
*
* @param {number} currentImageId
*/
show: function (currentImageId) {
var currentImage = this.images[currentImageId];
this.current = currentImageId;
this.errorLoadingImage = false;
if (this.playing) {
this._setTimeout();
}
this._setName(currentImage.name);
},
/**
* Stops and hides the slideshow
*/
stop: function () {
this._setName('');
this.playing = false;
this.slideshow.stop();
this.zoomablePreview.stop();
this._clearTimeout();
this.restoreContentCallback();
this.container.hide();
this.active = false;
},
/**
* Updates the private variables in case of problems loading an image
*
* @param {Array} images
* @param {boolean} errorLoadingImage
*/
updateControls: function (images, errorLoadingImage) {
this.images = images;
this.errorLoadingImage = errorLoadingImage;
},
/**
* Shows the action buttons
*
* @param {boolean} transparent
* @param {boolean} isPublic
* @param {number} permissions
*/
showActionButtons: function (transparent, isPublic, permissions) {
if (transparent) {
this._showBackgroundToggle();
}
var hideDownload = $('#hideDownload').val();
if (hideDownload !== 'true') {
this.showButton('.downloadImage');
}
var canDelete = ((permissions & OC.PERMISSION_DELETE) !== 0);
var canShare = ((permissions & OC.PERMISSION_SHARE) !== 0);
if (!isPublic && canDelete) {
this.showButton('.deleteImage');
}
if (!isPublic && canShare) {
this.showButton('.shareImage');
}
},
/**
* Hides the action buttons
*/
hideActionButtons: function () {
this.hideButton('.changeBackground');
this.hideButton('.downloadImage');
this.hideButton('.deleteImage');
this.hideButton('.shareImage');
},
/**
* Shows a button which has been hidden
*/
showButton: function (button) {
this.container.find(button).removeClass('hidden');
},
/**
* Hides a button
*
* @param button
*/
hideButton: function (button) {
this.container.find(button).addClass('hidden');
},
/**
* Removes a button
*
* @param button
*/
removeButton: function (button) {
this.container.find(button).remove();
},
/**
* Sets up the button based navigation
*
* @param {Function} makeCallBack
* @private
*/
_buttonSetup: function (makeCallBack) {
this.container.children('.next').click(makeCallBack(this._next));
this.container.children('.previous').click(makeCallBack(this._previous));
this.container.children('.exit').click(makeCallBack(this._exit));
this.container.children('.pause, .play').click(makeCallBack(this._playPauseToggle));
this.progressBar.click(makeCallBack(this._playPauseToggle));
this.container.children('.previous, .next, .slideshow-menu, .name').on(
'mousewheel DOMMouseScroll mousemove', function (evn) {
this.container.children('.bigshotContainer')[0].dispatchEvent(
new WheelEvent(evn.originalEvent.type, evn.originalEvent));
}.bind(this));
},
/**
* Sets up additional buttons
*
* @param {Function} makeCallBack
* @private
*/
_specialButtonSetup: function (makeCallBack) {
this.container.find('.downloadImage').click(makeCallBack(this._getImageDownload));
this.container.find('.deleteImage').click(makeCallBack(this._deleteImage));
this.container.find('.shareImage').click(makeCallBack(this.share));
this.container.find('.slideshow-menu').width = 52;
if (this.backgroundToggle) {
this.container.find('.changeBackground').click(
makeCallBack(this._toggleBackground));
this.container.find('.slideshow-menu').width += 52;
} else {
this.hideButton('.changeBackground');
}
},
/**
* Populates the share dialog with the needed information
*/
share: function () {
var image = this.images[this.current];
if (!Gallery.Share.droppedDown) {
$('.slideshow-menu a.share').data('path', image.path)
.data('link', true)
.data('item-source', image.file)
.data('possible-permissions', image.permissions)
.click();
}
},
/**
* Shows the background colour switcher, if activated in the configuration
*/
_showBackgroundToggle: function () {
if (this.backgroundToggle) {
this.showButton('.changeBackground');
}
},
/**
* Sets up the key based controls
*
* @param {Function} makeCallBack
* @private
*/
_keyCodeSetup: function (makeCallBack) {
$(document).keyup(function (evt) {
if (evt.target.tagName.toLowerCase() === 'input') {
return;
}
var escKey = 27;
var leftKey = 37;
var rightKey = 39;
var spaceKey = 32;
var fKey = 70;
var zoomOutKeys = [48, 96, 79, 40]; // zeros, o or down key
var zoomInKeys = [57, 105, 73, 38]; // 9, i or up key
if (evt.keyCode === escKey) {
makeCallBack(this._exit)(evt);
} else if (evt.keyCode === leftKey) {
makeCallBack(this._previous)(evt);
} else if (evt.keyCode === rightKey) {
makeCallBack(this._next)(evt);
} else if (evt.keyCode === spaceKey) {
makeCallBack(this._playPauseToggle)(evt);
} else if (evt.keyCode === fKey) {
makeCallBack(this._fullScreenToggle)(evt);
} else if (this._hasKeyBeenPressed(evt, zoomOutKeys)) {
makeCallBack(this._zoomToOriginal)(evt);
} else if (this._hasKeyBeenPressed(evt, zoomInKeys)) {
makeCallBack(this._zoomToFit)(evt);
}
}.bind(this));
},
/**
* Determines if a key has been pressed by comparing the event and the key
*
* @param evt
* @param {Array} keys
*
* @returns {boolean}
* @private
*/
_hasKeyBeenPressed: function (evt, keys) {
var i, keysLength = keys.length;
for (i = 0; i < keysLength; i++) {
if (evt.keyCode === keys[i]) {
return true;
}
}
return false;
},
/**
* Starts the slideshow timer
*
* @private
*/
_setTimeout: function () {
this._clearTimeout();
this.playTimeout = setTimeout(this._next.bind(this), this.interval);
this.progressBar.stop();
this.progressBar.css('height', '6px');
this.progressBar.animate({'height': '26px'}, this.interval, 'linear');
},
/**
* Stops the slideshow timer
*
* @private
*/
_clearTimeout: function () {
if (this.playTimeout) {
clearTimeout(this.playTimeout);
}
this.progressBar.stop();
this.progressBar.css('height', '6px');
this.playTimeout = 0;
},
/**
* Starts/stops autoplay and shows/hides the play/pause buttons
*
* @private
*/
_playPauseToggle: function () {
if (this.playing === true) {
this.playing = false;
this._clearTimeout();
} else {
this.playing = true;
this._setTimeout();
}
this.container.find('.play, .pause, .progress').toggleClass('hidden');
},
/**
* Shows the next slide
*
* @private
*/
_next: function () {
if(Gallery.Share){
Gallery.Share.hideDropDown();
}
this._setName('');
this.slideshow.hideErrorNotification();
this.zoomablePreview.reset();
if (this.errorLoadingImage) {
this.current -= 1;
}
this.current = (this.current + 1) % this.images.length;
var next = (this.current + 1) % this.images.length;
this._updateSlideshow(next);
},
/**
* Shows the previous slide
*
* @private
*/
_previous: function () {
if(Gallery.Share){
Gallery.Share.hideDropDown();
}
this._setName('');
this.slideshow.hideErrorNotification();
this.zoomablePreview.reset();
this.current = (this.current - 1 + this.images.length) % this.images.length;
var previous = (this.current - 1 + this.images.length) % this.images.length;
this._updateSlideshow(previous);
},
/**
* Asks the slideshow for the next image
*
* @param {number} imageId
* @private
*/
_updateSlideshow: function (imageId) {
this.slideshow.next(this.current, this.images[imageId]);
},
/**
* Exits the slideshow by going back in history
*
* @private
*/
_exit: function () {
if (Gallery.Share){
Gallery.Share.hideDropDown();
}
// Only modern browsers can manipulate history
if (history && history.replaceState) {
// We simulate a click on the back button in order to be consistent
window.history.back();
} else {
// For ancient browsers supported in core
this.stop();
}
},
/**
* Launches fullscreen mode if the browser supports it
*
* @private
*/
_fullScreenToggle: function () {
this.zoomablePreview.fullScreenToggle();
},
/**
* Resizes the image to its original size
*
* @private
*/
_zoomToOriginal: function () {
this.zoomablePreview.zoomToOriginal();
},
/**
* Fits the image in the browser window
*
* @private
*/
_zoomToFit: function () {
this.zoomablePreview.zoomToFit();
},
/**
* Sends the current image as a download
*
* @returns {boolean}
* @private
*/
_getImageDownload: function () {
var downloadUrl = this.images[this.current].downloadUrl;
return this.slideshow.getImageDownload(downloadUrl);
},
/**
* Changes the colour of the background of the image
*
* @private
*/
_toggleBackground: function () {
this.slideshow.toggleBackground();
},
/**
* Shows the filename of the current image
* @param {string} imageName
* @private
*/
_setName: function (imageName) {
var nameElement = this.container.find('.title');
nameElement.text(imageName);
},
/**
* Delete the image from the slideshow
* @private
*/
_deleteImage: function () {
var image = this.images[this.current];
var self = this;
$.ajax({
type: 'DELETE',
url: OC.getRootPath() + '/remote.php/webdav/' + image.path,
success: function () {
self.slideshow.deleteImage(image, self.current);
self.images.splice(self.current, 1);
if (self.images.length === 0) {
self._exit();
}
else {
self.current = self.current % self.images.length;
self._updateSlideshow(self.current);
}
}
});
}
};
SlideShow.Controls = Controls;
})(jQuery, SlideShow);
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global SlideShow, bigshot*/
(function ($, SlideShow, bigshot) {
"use strict";
/**
* Creates a zoomable preview
*
* @param {*} container
* @constructor
*/
var ZoomablePreview = function (container) {
this.container = container;
this.element = this.container.get(0);
var bigshotContainer = container.find('.bigshotContainer');
this.bigshotElement = bigshotContainer.get(0);
this._detectFullscreen();
this._setupControls();
$(window).resize(function () {
this._zoomDecider();
}.bind(this));
};
ZoomablePreview.prototype = {
container: null,
element: null,
bigshotContainer: null,
bigshotElement: null,
zoomable: null,
fullScreen: null,
canFullScreen: false,
currentImage: null,
mimeType: null,
maxZoom: 3,
smallImageDimension: 200 / window.devicePixelRatio,
smallImageScale: 2,
/**
* Launches the Bigshot zoomable preview
*
* @param {*} image
* @param {number} currentImage
* @param {string} mimeType
*/
startBigshot: function (image, currentImage, mimeType) {
this.currentImage = currentImage;
this.mimeType = mimeType;
if (this.zoomable !== null) {
this.zoomable.dispose();
this.zoomable = null;
}
var maxZoom = this.maxZoom;
var imgWidth = image.naturalWidth / window.devicePixelRatio;
var imgHeight = image.naturalHeight / window.devicePixelRatio;
// Set arbitrary image dimension when we have a SVG
if (imgWidth === 0 && mimeType === 'image/svg+xml') {
imgWidth = 2048;
imgHeight = 2048;
}
if (imgWidth < this.smallImageDimension &&
imgHeight < this.smallImageDimension &&
this.mimeType !== 'image/svg+xml') {
maxZoom += 3;
this.currentImage.isSmallImage = true;
}
this.zoomable = new bigshot.SimpleImage(new bigshot.ImageParameters({
container: this.bigshotElement,
maxZoom: maxZoom,
minZoom: 0,
touchUI: false,
width: imgWidth,
height: imgHeight
}), image);
// Reset our zoom based on image and window dimensions.
this._resetZoom();
// prevent zoom-on-doubleClick
this.zoomable.addEventListener('dblclick', function (ie) {
ie.preventDefault();
});
// Reset image position and size on orientation change
var self = this;
$(window).bind('orientationchange resize', function () {
self._resetZoom();
});
},
/**
* Resets the element for the next image to be displayed
*/
reset: function () {
if (this.zoomable !== null) {
this.zoomable.stopFlying();
this._resetZoom();
}
},
/**
* Throws away the zoomable preview
*/
stop: function () {
if (this.fullScreen !== null) {
this._fullScreenExit();
}
if (this.zoomable !== null) {
this.zoomable.dispose();
this.zoomable = null;
}
},
/**
* Launches fullscreen mode if the browser supports it
*/
fullScreenToggle: function () {
if (this.zoomable === null) {
return;
}
if (this.fullScreen !== null) {
this._fullScreenExit();
} else {
this._fullScreenStart();
}
},
/**
* Resizes the image to its original size
*/
zoomToOriginal: function () {
if (this.zoomable === null) {
return;
}
if (this.currentImage.isSmallImage) {
this.zoomable.flyTo(0, 0, this.smallImageScale, true);
} else if ($(window).width() < this.zoomable.width ||
$(window).height() < this.zoomable.height) {
// The image is larger than the window.
// Set minimum zoom and call flyZoomToFit.
this.zoomable.setMinZoom(this.zoomable.getZoomToFitValue());
this.zoomable.flyZoomToFit();
} else {
this.zoomable.setMinZoom(0);
this.zoomable.flyTo(0, 0, 0, true);
}
},
/**
* Fits the image in the browser window
*/
zoomToFit: function () {
if (this.zoomable !== null) {
this.zoomable.flyZoomToFit();
}
},
/**
* Detect fullscreen capability
* @private
*/
_detectFullscreen: function () {
this.canFullScreen = this.element.requestFullscreen !== undefined ||
this.element.mozRequestFullScreen !== undefined ||
this.element.webkitRequestFullscreen !== undefined ||
this.element.msRequestFullscreen !== undefined;
},
/**
* Makes UI controls work on touchscreens. Pinch only works on iOS
* @private
*/
_setupControls: function () {
var browser = new bigshot.Browser();
this.container.children('input').each(function (i, e) {
browser.registerListener(e, 'click', browser.stopEventBubblingHandler(), false);
browser.registerListener(e, 'touchstart', browser.stopEventBubblingHandler(),
false);
browser.registerListener(e, 'touchend', browser.stopEventBubblingHandler(), false);
});
},
/**
* Determines whether the image should be shown at its original size or if it should fill
* the screen
*
* @private
*/
_zoomDecider: function () {
if (this.zoomable !== null) {
if (this.fullScreen === null && this.mimeType !== 'image/svg+xml') {
this.zoomToOriginal();
} else {
this.zoomToFit();
}
}
},
/**
* Resets the image to its original zoomed size
*
* @private
*/
_resetZoom: function () {
if (this.zoomable === null) {
return;
}
if (this.currentImage.isSmallImage) {
this.zoomable.setZoom(this.smallImageScale, true);
} else if ($(window).width() < this.zoomable.width ||
$(window).height() < this.zoomable.height ||
this.fullScreen !== null ||
this.mimeType === 'image/svg+xml') {
// The image is larger than the window, or we are fullScreen,
// or this is an SVG. Set minimum zoom and call zoomToFit.
this.zoomable.setMinZoom(this.zoomable.getZoomToFitValue());
this.zoomable.zoomToFit();
} else {
// Zoom to the image size.
this.zoomable.setMinZoom(0);
this.zoomable.setZoom(0, true);
}
},
/**
* Starts the fullscreen previews
*
* @private
*/
_fullScreenStart: function () {
if (!this.canFullScreen) {
return;
}
this.fullScreen = new bigshot.FullScreen(this.element);
this.fullScreen.open();
this.fullScreen.addOnClose(function () {
this._fullScreenExit();
}.bind(this));
},
/**
* Stops the fullscreen previews
*
* @private
*/
_fullScreenExit: function () {
if (this.fullScreen === null) {
return;
}
this.fullScreen.close();
this.fullScreen = null;
this._zoomDecider();
}
};
SlideShow.ZoomablePreview = ZoomablePreview;
})(jQuery, SlideShow, bigshot);
/**
* Nextcloud - Gallery
*
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Olivier Paroz <galleryapps@oparoz.com>
*
* @copyright Olivier Paroz 2017
*/
/* global OC, OCA, FileList, $, t */
var GalleryButton = {};
GalleryButton.isPublic = false;
GalleryButton.button = {};
GalleryButton.url = null;
/**
* Rebuilds the Gallery URL every time the files list has changed
*/
GalleryButton.onFileListUpdated = function () {
"use strict";
var fileList;
if (GalleryButton.isPublic) {
fileList = OCA.Sharing.PublicApp.fileList;
} else {
fileList = FileList;
}
GalleryButton.buildGalleryUrl(fileList.getCurrentDirectory().replace(/^\//, ''));
};
/**
* Builds the URL which will load the exact same folder in Gallery
*
* @param dir
*/
GalleryButton.buildGalleryUrl = function (dir) {
"use strict";
var params = {};
var tokenPath = '';
var sharingTokenElement = $('#sharingToken');
var token = (sharingTokenElement.val()) ? sharingTokenElement.val() : false;
if (token) {
params.token = token;
tokenPath = 's/{token}';
}
GalleryButton.url =
OC.generateUrl('apps/gallery/' + tokenPath, params) + '#' + encodeURIComponent(dir);
};
$(document).ready(function () {
"use strict";
if ($('#body-login').length > 0) {
return true; //deactivate on login page
}
if ($('html').is('.ie8')) {
return true; //deactivate in IE8
}
if ($('#isPublic').val()) {
GalleryButton.isPublic = true;
}
if ($('#filesApp').val()) {
$('#fileList').on('updated', GalleryButton.onFileListUpdated);
// Button for opening files list as gallery view
GalleryButton.button =
$('<div id="gallery-button" class="button view-switcher">' +
'<div id="button-loading" class="hidden"></div>' +
'<span class="icon-toggle-pictures"></span>' +
'</div>');
GalleryButton.button.click(function () {
$(this).children('span').addClass('hidden');
$(this).children('#button-loading').removeClass('hidden').addClass('icon-loading-small');
window.location.href = GalleryButton.url;
});
$('#controls').append(GalleryButton.button);
}
}
);
/* global Gallery, escapeHTML */
(function ($, Gallery) {
"use strict";
/**
* @typedef {Object} Gallery.Share.Types.ShareInfo
* @property {Number} share_type
* @property {Number} permissions
* @property {Number} file_source optional
* @property {Number} item_source
* @property {String} token
* @property {String} share_with
* @property {String} share_with_displayname
* @property {String} mail_send
* @property {String} displayname_file_owner
* @property {String} displayname_owner
* @property {String} uid_owner
* @property {String} uid_file_owner
* @property {String} expiration optional
* @property {Number} stime
*/
// copied and stripped out from the old core
var Share = {
SHARE_TYPE_USER: 0,
SHARE_TYPE_GROUP: 1,
SHARE_TYPE_LINK: 3,
SHARE_TYPE_EMAIL: 4,
SHARE_TYPE_REMOTE: 6,
/**
* @deprecated use OC.Share.currentShares instead
*/
itemShares: [],
/**
* Shares for the currently selected file.
* (for which the dropdown is open)
*
* Key is item type and value is an array or
* shares of the given item type.
*/
currentShares: {},
/**
* Whether the share dropdown is opened.
*/
droppedDown: false,
/** @type {object} **/
_lastSuggestions: undefined,
/** @type {int} **/
_pendingOperationsCount: 0,
/**
*
* @param path {String} path to the file/folder which should be shared
* @param shareType {Number} 0 = user; 1 = group; 3 = public link; 6 = federated cloud
* share
* @param shareWith {String} user / group id with which the file should be shared
* @param publicUpload {Boolean} allow public upload to a public shared folder
* @param password {String} password to protect public link Share with
* @param permissions {Number} 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31
* = all (default: 31, for public shares: 1)
* @param callback {Function} method to call back after a successful share creation
* @param errorCallback {Function} method to call back after a failed share creation
*
* @returns {*}
*/
share: function (path, shareType, shareWith, publicUpload, password, permissions, callback, errorCallback) {
return $.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares' + '?format=json',
type: 'POST',
data: {
path: path,
shareType: shareType,
shareWith: shareWith,
publicUpload: publicUpload,
password: password,
permissions: permissions
},
dataType: 'json'
}).done(function (result) {
if (callback) {
callback(result.ocs.data);
}
}).fail(function (xhr) {
var result = xhr.responseJSON;
if (_.isFunction(errorCallback)) {
errorCallback(result);
} else {
var msg = t('gallery', 'Error');
if (result.ocs && result.ocs.meta.message) {
msg = result.ocs.meta.message;
}
OC.dialogs.alert(msg, t('gallery', 'Error while sharing'));
}
});
},
/**
*
* @param {Number} shareId
* @param {Function} callback
*/
unshare: function (shareId, callback) {
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'DELETE'
}).done(function () {
if (callback) {
callback();
}
}).fail(function () {
OC.dialogs.alert(t('gallery', 'Error while unsharing'), t('gallery', 'Error'));
});
},
/**
*
* @param {Number} shareId
* @param {Number} permissions
*/
setPermissions: function (shareId, permissions) {
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
permissions: permissions
}
}).fail(function () {
OC.dialogs.alert(t('gallery', 'Error while changing permissions'),
t('gallery', 'Error'));
});
},
/**
*
* @param {String} searchTerm
* @param {String} itemType
*/
_getSuggestions: function (searchTerm, itemType) {
if (this._lastSuggestions &&
this._lastSuggestions.searchTerm === searchTerm &&
this._lastSuggestions.itemType === itemType) {
return this._lastSuggestions.promise;
}
var deferred = $.Deferred();
$.get(OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees', {
format: 'json',
search: searchTerm,
perPage: 200,
itemType: itemType
}, function (result) {
if (result.ocs.meta.statuscode === 100) {
var filter = function(users) {
var usersLength;
var i;
//Filter out the current user
usersLength = users.length;
for (i = 0; i < usersLength; i++) {
if (users[i].value.shareWith === OC.currentUser) {
users.splice(i, 1);
break;
}
}
}
filter(result.ocs.data.exact.users);
var exactUsers = result.ocs.data.exact.users;
var exactGroups = result.ocs.data.exact.groups;
var exactRemotes = result.ocs.data.exact.remotes;
var exactEmails = [];
if (typeof(result.ocs.data.emails) !== 'undefined') {
exactEmails = result.ocs.data.exact.emails;
}
var exactCircles = [];
if (typeof(result.ocs.data.circles) !== 'undefined') {
exactCircles = result.ocs.data.exact.circles;
}
var exactMatches = exactUsers.concat(exactGroups).concat(exactRemotes).concat(exactEmails).concat(exactCircles);
filter(result.ocs.data.users);
var users = result.ocs.data.users;
var groups = result.ocs.data.groups;
var remotes = result.ocs.data.remotes;
var lookup = result.ocs.data.lookup;
var emails = [];
if (typeof(result.ocs.data.emails) !== 'undefined') {
emails = result.ocs.data.emails;
}
var circles = [];
if (typeof(result.ocs.data.circles) !== 'undefined') {
circles = result.ocs.data.circles;
}
var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(emails).concat(circles).concat(lookup);
deferred.resolve(suggestions, exactMatches);
} else {
deferred.reject(result.ocs.meta.message);
}
}).fail(function () {
deferred.reject();
});
this._lastSuggestions = {
searchTerm: searchTerm,
itemType: itemType,
promise: deferred.promise()
};
return this._lastSuggestions.promise;
},
/**
*
* @param {int} shareType
* @param {int} possiblePermissions
*/
_getPermissions: function (shareType, possiblePermissions) {
// Default permissions are Edit (CRUD) and Share
// Check if these permissions are possible
var permissions = OC.PERMISSION_READ;
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) {
permissions =
OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
} else {
if (possiblePermissions & OC.PERMISSION_UPDATE) {
permissions = permissions | OC.PERMISSION_UPDATE;
}
if (possiblePermissions & OC.PERMISSION_CREATE) {
permissions = permissions | OC.PERMISSION_CREATE;
}
if (possiblePermissions & OC.PERMISSION_DELETE) {
permissions = permissions | OC.PERMISSION_DELETE;
}
if (oc_appconfig.core.resharingAllowed &&
(possiblePermissions & OC.PERMISSION_SHARE)) {
permissions = permissions | OC.PERMISSION_SHARE;
}
}
return permissions;
},
/**
*
* @param {String} itemType
* @param {String} path
* @param {String} appendTo
* @param {String} link
* @param {Number} possiblePermissions
* @param {String} filename
*/
showDropDown: function (itemType, path, appendTo, link, possiblePermissions, filename) {
// This is a sync AJAX request on the main thread...
var data = this._loadShares(path);
var dropDownEl;
var self = this;
var html = '<div id="dropdown" class="drop shareDropDown" data-item-type="' + escapeHTML(itemType) +
'" data-item-source="' + escapeHTML(path) + '">';
if (data !== false && data[0] && !_.isUndefined(data[0].uid_file_owner) &&
data[0].uid_file_owner !== OC.currentUser
) {
html += '<span class="reshare">';
if (oc_config.enable_avatars === true) {
html += '<div class="avatar"></div>';
}
if (data[0].share_type == this.SHARE_TYPE_GROUP) {
html += t('gallery', 'Shared with you and the group {group} by {owner}', {
group: data[0].share_with,
owner: data[0].displayname_owner
});
} else {
html += t('gallery', 'Shared with you by {owner}',
{owner: data[0].displayname_owner});
}
html += '</span><br />';
// reduce possible permissions to what the original share allowed
possiblePermissions = possiblePermissions & data[0].permissions;
}
if (possiblePermissions & OC.PERMISSION_SHARE) {
// Determine the Allow Public Upload status.
// Used later on to determine if the
// respective checkbox should be checked or
// not.
var publicUploadEnabled = $('#gallery').data('allow-public-upload');
if (typeof publicUploadEnabled == 'undefined') {
publicUploadEnabled = 'no';
}
var allowPublicUploadStatus = false;
$.each(data, function (key, value) {
if (value.share_type === self.SHARE_TYPE_LINK) {
allowPublicUploadStatus =
(value.permissions & OC.PERMISSION_CREATE) ? true : false;
return true;
}
});
var sharePlaceholder = t('gallery', 'Share with users or groups …');
if (oc_appconfig.core.remoteShareAllowed) {
sharePlaceholder = t('gallery', 'Share with users, groups or remote users …');
}
html += '<label for="shareWith" class="hidden-visually">' + t('gallery', 'Share') +
'</label>';
html +=
'<input id="shareWith" type="text" placeholder="' + sharePlaceholder + '" />';
html += '<span class="shareWithConfirm icon-confirm svg"></span>';
html += '<span class="shareWithLoading icon-loading-small hidden"></span>';
html += '<ul id="shareWithList">';
html += '</ul>';
var linksAllowed = $('#allowShareWithLink').val() === 'yes';
if (link && linksAllowed) {
html += '<div id="link" class="linkShare">';
html += '<span class="icon-loading-small hidden"></span>';
html +=
'<input type="checkbox" class="checkbox checkbox--right" ' +
'name="linkCheckbox" id="linkCheckbox" value="1" />' +
'<label for="linkCheckbox">' + t('gallery', 'Share link') + '</label>';
html += '<br />';
var defaultExpireMessage = '';
if ((itemType === 'folder' || itemType === 'file') &&
oc_appconfig.core.defaultExpireDateEnforced) {
defaultExpireMessage =
t('gallery',
'The public link will expire no later than {days} days after it is created',
{'days': oc_appconfig.core.defaultExpireDate}) + '<br/>';
}
html += '<label for="linkText" class="hidden-visually">' + t('gallery', 'Link') +
'</label>';
html += '<div id="linkText-container">';
html += '<input id="linkText" type="text" readonly="readonly" />';
html += '<a id="linkTextMore" class="button icon-more" href="#"></a>';
html += '<div id="linkSocial" class="popovermenu bubble menu hidden"></div>';
html += '</div>';
html +=
'<input type="checkbox" class="checkbox checkbox--right" ' +
'name="showPassword" id="showPassword" value="1" />' +
'<label for="showPassword" style="display:none;">' +
t('gallery', 'Password protect') + '</label>';
html += '<div id="linkPass">';
html += '<label for="linkPassText" class="hidden-visually">' +
t('gallery', 'Password') + '</label>';
html += '<input id="linkPassText" type="password" placeholder="' +
t('gallery', 'Choose a password for the public link') + '" />';
html += '<span class="icon-loading-small hidden"></span>';
html += '</div>';
if (itemType === 'folder' && (possiblePermissions & OC.PERMISSION_CREATE) &&
publicUploadEnabled === 'yes') {
html += '<div id="allowPublicUploadWrapper" style="display:none;">';
html += '<span class="icon-loading-small hidden"></span>';
html +=
'<input type="checkbox" class="checkbox checkbox--right" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload"' +
((allowPublicUploadStatus) ? 'checked="checked"' : '') + ' />';
html += '<label for="sharingDialogAllowPublicUpload">' +
t('gallery', 'Allow editing') + '</label>';
html += '</div>';
}
var mailPublicNotificationEnabled = $('input:hidden[name=mailPublicNotificationEnabled]').val();
if (mailPublicNotificationEnabled === 'yes') {
html += '<form id="emailPrivateLink">';
html +=
'<input id="email" style="display:none; width:62%;" value="" placeholder="' +
t('gallery', 'Email link to person') + '" type="text" />';
html +=
'<input id="emailButton" style="display:none;" type="submit" value="' +
t('gallery', 'Send') + '" />';
html += '</form>';
}
}
html += '<div id="expiration">';
html +=
'<input type="checkbox" class="checkbox checkbox--right" ' +
'name="expirationCheckbox" id="expirationCheckbox" value="1" />' +
'<label for="expirationCheckbox">' +
t('gallery', 'Set expiration date') + '</label>';
html += '<label for="expirationDate" class="hidden-visually">' +
t('gallery', 'Expiration') + '</label>';
html += '<input id="expirationDate" type="text" placeholder="' +
t('gallery', 'Expiration date') + '" style="display:none; width:90%;" />';
html += '<em id="defaultExpireMessage">' + defaultExpireMessage + '</em>';
html += '</div>';
dropDownEl = $(html);
dropDownEl = dropDownEl.appendTo(appendTo);
//Get owner avatars
if (oc_config.enable_avatars === true && data !== false && data[0] !== false &&
!_.isUndefined(data[0]) && !_.isUndefined(data[0].uid_file_owner)) {
dropDownEl.find(".avatar").avatar(data[0].uid_file_owner, 32);
}
// Reset item shares
this.itemShares = [];
this.currentShares = {};
if (data) {
$.each(data, function (index, share) {
if (share.share_type === self.SHARE_TYPE_LINK) {
self.showLink(share.id, share.token, share.share_with);
} else {
if (share.share_with !== OC.currentUser) {
if (share.share_type === self.SHARE_TYPE_REMOTE) {
self._addShareWith(share.id,
share.share_type,
share.share_with,
share.share_with_displayname,
share.permissions,
OC.PERMISSION_READ | OC.PERMISSION_UPDATE |
OC.PERMISSION_CREATE,
share.mail_send,
false);
} else {
self._addShareWith(share.id,
share.share_type,
share.share_with,
share.share_with_displayname,
share.permissions,
possiblePermissions,
share.mail_send,
false);
}
}
}
if (share.expiration != null) {
var expireDate = moment(share.expiration, 'YYYY-MM-DD').format(
'DD-MM-YYYY');
self.showExpirationDate(expireDate, share.stime);
}
});
}
$('#shareWith').autocomplete({
minLength: 2,
delay: 750,
source: function (search, response) {
var $shareWithField = $('#dropdown #shareWith');
var $loading = $('#dropdown .shareWithLoading');
var $confirm = $('#dropdown .shareWithConfirm');
$loading.removeClass('hidden');
$confirm.addClass('hidden');
self._pendingOperationsCount++;
$shareWithField.removeClass('error')
.tooltip('hide');
self._getSuggestions(
search.term.trim(),
itemType
).done(function(suggestions) {
self._pendingOperationsCount--;
if (self._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$confirm.removeClass('hidden');
}
if (suggestions.length > 0) {
$('#shareWith')
.autocomplete("option", "autoFocus", true);
response(suggestions);
// show a notice that the list is truncated
// this is the case if one of the search results is at least as long as the max result config option
if (oc_config['sharing.maxAutocompleteResults'] > 0 &&
Math.min(perPage, oc_config['sharing.maxAutocompleteResults'])
<= Math.max(users.length, groups.length, remotes.length, emails.length, lookup.length)) {
var message = t('gallery', 'This list is maybe truncated - please refine your search term to see more results.');
$('.ui-autocomplete').append('<li class="autocomplete-note">' + message + '</li>');
}
} else {
var title = t('gallery', 'No users or groups found for {search}', {search: $('#shareWith').val()});
if (!oc_appconfig.core.allowGroupSharing) {
title = t('gallery', 'No users found for {search}', {search: $('#shareWith').val()});
}
$('#shareWith').addClass('error')
.attr('data-original-title', title)
.tooltip('hide')
.tooltip({
placement: 'bottom',
trigger: 'manual'
})
.tooltip('fixTitle')
.tooltip('show');
response();
}
}).fail(function (message) {
self._pendingOperationsCount--;
if (self._pendingOperationsCount === 0) {
$('#dropdown').find('.shareWithLoading').addClass('hidden');
$('#dropdown').find('.shareWithConfirm').removeClass('hidden');
}
if (message) {
OC.Notification.showTemporary(t('gallery', 'An error occurred ("{message}"). Please try again', { message: message }));
} else {
OC.Notification.showTemporary(t('gallery', 'An error occurred. Please try again'));
}
});
},
focus: function (event) {
event.preventDefault();
},
select: function (event, selected) {
// Ensure that the keydown handler for the input field
// is not called; otherwise it would try to add the
// recipient again, which would fail.
event.stopImmediatePropagation();
var $dropDown = $('#dropdown');
var itemSource = $dropDown.data('item-source');
var expirationDate = '';
if ($('#expirationCheckbox').is(':checked') === true) {
expirationDate = $("#expirationDate").val();
}
var shareType = selected.item.value.shareType;
var shareWith = selected.item.value.shareWith;
$(this).val(shareWith);
var permissions = self._getPermissions(shareType, possiblePermissions);
var $input = $(this);
var $loading = $dropDown.find('.shareWithLoading');
var $confirm = $dropDown.find('.shareWithConfirm');
$loading.removeClass('hidden');
$confirm.addClass('hidden');
$input.val(t('gallery', 'Adding user...'));
$input.prop('disabled', true);
self._pendingOperationsCount++;
Gallery.Share.share(
itemSource,
shareType,
shareWith,
0,
null,
permissions,
function (data) {
// Adding a share changes the suggestions.
self._lastSuggestions = undefined;
var posPermissions = possiblePermissions;
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) {
posPermissions = permissions;
}
Gallery.Share._addShareWith(data.id, shareType, shareWith,
selected.item.label,
permissions, posPermissions);
$input.val('');
$input.prop('disabled', false);
self._pendingOperationsCount--;
if (self._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$confirm.removeClass('hidden');
}
},
function (result) {
var message = t('gallery', 'Error');
if (result && result.ocs && result.ocs.meta && result.ocs.meta.message) {
message = result.ocs.meta.message;
}
OC.Notification.showTemporary(message);
$input.val(shareWith);
$input.prop('disabled', false);
self._pendingOperationsCount--;
if (self._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$confirm.removeClass('hidden');
}
});
return false;
}
}).data("ui-autocomplete")._renderItem = function (ul, item) {
// customize internal _renderItem function to display groups and users
// differently
var insert = $("<a>");
var text = item.label;
if (item.value.shareType === Gallery.Share.SHARE_TYPE_GROUP) {
text = text + ' (' + t('gallery', 'group') + ')';
} else if (item.value.shareType === Gallery.Share.SHARE_TYPE_REMOTE) {
text = text + ' (' + t('gallery', 'remote') + ')';
}
insert.text(text);
if (item.value.shareType === Gallery.Share.SHARE_TYPE_GROUP) {
insert = insert.wrapInner('<strong></strong>');
}
return $("<li>")
.addClass(
(item.value.shareType ===
Gallery.Share.SHARE_TYPE_GROUP) ? 'group' : 'user')
.append(insert)
.appendTo(ul);
};
var shareFieldKeydownHandler = function(event) {
if (event.keyCode !== 13) {
return true;
}
self._confirmShare(itemType, possiblePermissions);
return false;
};
$('#shareWith').on('keydown', shareFieldKeydownHandler);
$('#shareWith').on('input', function () {
if ($(this).val().length < 2) {
$(this).removeClass('error').tooltip('hide');
}
});
/* trigger search after the field was re-selected */
$('#shareWith').on('focus', function() {
$(this).autocomplete('search');
});
$('.shareWithConfirm').on('click', function () {
self._confirmShare(itemType, possiblePermissions);
});
if (link && linksAllowed && $('#email').length != 0) {
$('#email').autocomplete({
minLength: 1,
source: function (search, response) {
$.get(OC.filePath('core', 'ajax', 'share.php'), {
fetch: 'getShareWithEmail',
search: search.term
}, function (result) {
if (result.status == 'success' && result.data.length > 0) {
response(result.data);
}
});
},
select: function (event, item) {
$('#email').val(item.item.email);
return false;
}
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
return $('<li>')
.append('<a>' + escapeHTML(item.displayname) + "<br>" +
escapeHTML(item.email) + '</a>')
.appendTo(ul);
};
}
} else {
html += '<input id="shareWith" type="text" placeholder="' +
t('gallery', 'Resharing is not allowed') +
'" style="width:90%;" disabled="disabled"/>';
html += '</div>';
dropDownEl = $(html);
dropDownEl.appendTo(appendTo);
}
dropDownEl.attr('data-item-source-name', filename);
$('#dropdown').slideDown(OC.menuSpeed, function () {
Gallery.Share.droppedDown = true;
});
if ($('html').hasClass('lte9')) {
$('#dropdown input[placeholder]').placeholder();
}
$('#shareWith').focus();
},
_confirmShare: function (itemType, possiblePermissions) {
var self = this;
var $shareWithField = $('#dropdown #shareWith');
var $loading = $('#dropdown .shareWithLoading');
var $confirm = $('#dropdown .shareWithConfirm');
$loading.removeClass('hidden');
$confirm.addClass('hidden');
this._pendingOperationsCount++;
$shareWithField.prop('disabled', true);
// Disabling the autocompletion does not clear its search timeout;
// removing the focus from the input field does, but only if the
// autocompletion is not disabled when the field loses the focus.
// Thus, the field has to be disabled before disabling the
// autocompletion to prevent an old pending search result from
// being processed once the field is enabled again.
$shareWithField.autocomplete('close');
$shareWithField.autocomplete('disable');
var itemSource = $('#dropdown').data('item-source');
var expirationDate = '';
if ($('#expirationCheckbox').is(':checked') === true) {
expirationDate = $("#expirationDate").val();
}
var restoreUI = function() {
self._pendingOperationsCount--;
if (self._pendingOperationsCount === 0) {
$loading.addClass('hidden');
$confirm.removeClass('hidden');
}
$shareWithField.prop('disabled', false);
$shareWithField.focus();
};
this._getSuggestions(
$shareWithField.val(),
itemType
).done(function(suggestions, exactMatches) {
if (suggestions.length === 0) {
restoreUI();
$shareWithField.autocomplete('enable');
// There is no need to show an error message here; it will
// be automatically shown when the autocomplete is activated
// again (due to the focus on the field) and it finds no
// matches.
return;
}
if (exactMatches.length !== 1) {
restoreUI();
$shareWithField.autocomplete('enable');
return;
}
var shareType = exactMatches[0].value.shareType;
var shareWith = exactMatches[0].value.shareWith;
var permissions = self._getPermissions(shareType, possiblePermissions);
var actionSuccess = function(data) {
var updatedPossiblePermissions = possiblePermissions;
if (shareType === Gallery.Share.SHARE_TYPE_REMOTE) {
updatedPossiblePermissions = permissions;
}
Gallery.Share._addShareWith(data.id, shareType, shareWith,
exactMatches[0].label,
permissions, updatedPossiblePermissions);
// Adding a share changes the suggestions.
self._lastSuggestions = undefined;
$shareWithField.val('');
restoreUI();
$shareWithField.autocomplete('enable');
};
var actionError = function(result) {
restoreUI();
$shareWithField.autocomplete('enable');
var message = t('gallery', 'Error');
if (result && result.ocs && result.ocs.meta && result.ocs.meta.message) {
message = result.ocs.meta.message;
}
OC.Notification.showTemporary(message);
};
Gallery.Share.share(
itemSource,
shareType,
shareWith,
0,
null,
permissions,
actionSuccess,
actionError
);
}).fail(function (message) {
restoreUI();
$shareWithField.autocomplete('enable');
// There is no need to show an error message here; it will be
// automatically shown when the autocomplete is activated again
// (due to the focus on the field) and getting the suggestions
// fail.
});
},
/**
*
* @param callback
*/
hideDropDown: function (callback) {
this.currentShares = null;
$('#dropdown').slideUp(OC.menuSpeed, function () {
Gallery.Share.droppedDown = false;
$('#dropdown').remove();
if (typeof FileActions !== 'undefined') {
$('tr').removeClass('mouseOver');
}
if (callback) {
callback.call();
}
});
},
/**
*
* @param id
* @param token
* @param password
*/
showLink: function (id, token, password) {
var $linkCheckbox = $('#linkCheckbox');
this.itemShares[this.SHARE_TYPE_LINK] = true;
$linkCheckbox.attr('checked', true);
$linkCheckbox.data('id', id);
var $linkText = $('#linkText');
if (Gallery.appName === 'files') {
var link = parent.location.protocol + '//' + location.host +
OC.generateUrl('/s/') + token;
} else {
var link = parent.location.protocol + '//' + location.host +
OC.generateUrl('/apps/gallery/s/') + token;
}
$linkText.val(link);
$linkText.slideDown(OC.menuSpeed);
$linkText.css('display', 'block');
if (oc_appconfig.core.enforcePasswordForPublicLink === false || password === null) {
$('#showPassword+label').show();
}
if (password != null) {
$('#linkPass').slideDown(OC.menuSpeed);
$('#showPassword').attr('checked', true);
$('#linkPassText').attr('placeholder', '**********');
}
$('#expiration').show();
$('#emailPrivateLink #email').show();
$('#emailPrivateLink #emailButton').show();
$('#allowPublicUploadWrapper').show();
$('#linkTextMore').show();
$('#linkSocial').hide();
$('#linkSocial').html('');
var ul = $('<ul/>');
OC.Share.Social.Collection.each(function(model) {
var url = model.get('url');
url = url.replace('{{reference}}', link);
var li = $('<li>' +
'<a href="#" class="menuitem pop-up" data-url="' + url + '" data-window="'+model.get('newWindow')+'">' +
'<span class="icon ' + model.get('iconClass') + '"></span>' +
'<span>' + model.get('name') + '</span>' +
'</a>');
li.appendTo(ul);
});
ul.appendTo('#linkSocial');
if (OC.Share.Social.Collection.length === 0) {
$('#linkTextMore').hide();
$linkText.addClass('no-menu-item');
} else {
$linkText.removeClass('no-menu-item');
}
},
/**
*
*/
hideLink: function () {
$('#linkText').slideUp(OC.menuSpeed);
$('#defaultExpireMessage').hide();
$('#showPassword+label').hide();
$('#linkSocial').hide();
$('#linkTextMore').hide();
$('#linkPass').slideUp(OC.menuSpeed);
$('#emailPrivateLink #email').hide();
$('#emailPrivateLink #emailButton').hide();
$('#allowPublicUploadWrapper').hide();
},
/**
* Displays the expiration date field
*
* @param {String} date current expiration date
* @param {Date|Number|String} [shareTime] share timestamp in seconds, defaults to now
*/
showExpirationDate: function (date, shareTime) {
var $expirationDate = $('#expirationDate');
var $expirationCheckbox = $('#expirationCheckbox');
var now = new Date();
// min date should always be the next day
var minDate = new Date();
minDate.setDate(minDate.getDate() + 1);
var datePickerOptions = {
minDate: minDate,
maxDate: null
};
// TODO: hack: backend returns string instead of integer
shareTime = this._parseTime(shareTime);
if (_.isNumber(shareTime)) {
shareTime = new Date(shareTime * 1000);
}
if (!shareTime) {
shareTime = now;
}
$expirationCheckbox.attr('checked', true);
$expirationDate.val(date);
$expirationDate.slideDown(OC.menuSpeed);
$expirationDate.css('display', 'block');
$expirationDate.datepicker({
dateFormat: 'dd-mm-yy'
});
if (oc_appconfig.core.defaultExpireDateEnforced) {
$expirationCheckbox.attr('disabled', true);
shareTime = OC.Util.stripTime(shareTime).getTime();
// max date is share date + X days
datePickerOptions.maxDate =
new Date(shareTime + oc_appconfig.core.defaultExpireDate * 24 * 3600 * 1000);
}
if (oc_appconfig.core.defaultExpireDateEnabled) {
$('#defaultExpireMessage').slideDown(OC.menuSpeed);
}
$.datepicker.setDefaults(datePickerOptions);
},
/**
* Get the default Expire date
*
* @return {String} The expire date
*/
getDefaultExpirationDate: function () {
var expireDateString = '';
if (oc_appconfig.core.defaultExpireDateEnabled) {
var date = new Date().getTime();
var expireAfterMs = oc_appconfig.core.defaultExpireDate * 24 * 60 * 60 * 1000;
var expireDate = new Date(date + expireAfterMs);
var month = expireDate.getMonth() + 1;
var year = expireDate.getFullYear();
var day = expireDate.getDate();
expireDateString = year + "-" + month + '-' + day + ' 00:00:00';
}
return expireDateString;
},
/**
* Loads all shares associated with a path
*
* @param path
*
* @returns {Gallery.Share.Types.ShareInfo|Boolean}
* @private
*/
_loadShares: function (path) {
var data = false;
var url = OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares' + '?format=json';
$.ajax({
url: url,
type: 'GET',
data: {
path: path,
shared_with_me: true
},
async: false
}).done(function (result) {
data = result.ocs.data;
$.ajax({
url: url,
type: 'GET',
data: {
path: path,
reshares: true
},
async: false
}).done(function (result) {
data = _.union(data, result.ocs.data);
})
});
if (data === false) {
OC.dialogs.alert(t('gallery', 'Error while retrieving shares'),
t('gallery', 'Error'));
}
return data;
},
/**
*
* @param shareId
* @param shareType
* @param shareWith
* @param shareWithDisplayName
* @param permissions
* @param possiblePermissions
* @param mailSend
*
* @private
*/
_addShareWith: function (shareId, shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend) {
var shareItem = {
share_id: shareId,
share_type: shareType,
share_with: shareWith,
share_with_displayname: shareWithDisplayName,
permissions: permissions
};
if (shareType === this.SHARE_TYPE_GROUP) {
shareWithDisplayName = shareWithDisplayName + " (" + t('gallery', 'group') + ')';
}
if (shareType === this.SHARE_TYPE_REMOTE) {
shareWithDisplayName = shareWithDisplayName + " (" + t('gallery', 'remote') + ')';
}
if (!this.itemShares[shareType]) {
this.itemShares[shareType] = [];
}
this.itemShares[shareType].push(shareWith);
var editChecked = '',
createChecked = '',
updateChecked = '',
deleteChecked = '',
shareChecked = '';
if (permissions & OC.PERMISSION_CREATE) {
createChecked = 'checked="checked"';
editChecked = 'checked="checked"';
}
if (permissions & OC.PERMISSION_UPDATE) {
updateChecked = 'checked="checked"';
editChecked = 'checked="checked"';
}
if (permissions & OC.PERMISSION_DELETE) {
deleteChecked = 'checked="checked"';
editChecked = 'checked="checked"';
}
if (permissions & OC.PERMISSION_SHARE) {
shareChecked = 'checked="checked"';
}
var html = '<li style="clear: both;" ' +
'data-id="' + escapeHTML(shareId) + '"' +
'data-share-type="' + escapeHTML(shareType) + '"' +
'data-share-with="' + escapeHTML(shareWith) + '"' +
'title="' + escapeHTML(shareWith) + '">';
var showCrudsButton;
html +=
'<a href="#" class="unshare"><img class="svg" alt="' + t('gallery', 'Unshare') +
'" title="' + t('gallery', 'Unshare') + '" src="' +
OC.imagePath('core', 'actions/delete') + '"/></a>';
if (oc_config.enable_avatars === true) {
html += '<div class="avatar"></div>';
}
html += '<span class="username">' + escapeHTML(shareWithDisplayName) + '</span>';
var mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val();
if (mailNotificationEnabled === 'yes' &&
shareType !== this.SHARE_TYPE_REMOTE) {
var checked = '';
if (mailSend === 1) {
checked = 'checked';
}
html +=
'<input id="mail-' + escapeHTML(shareWith) + '" type="checkbox" class="mailNotification checkbox checkbox--right" ' +
'name="mailNotification" ' +
checked + ' />';
html +=
'<label for="mail-' + escapeHTML(shareWith) + '">' + t('gallery', 'notify by email') + '</label>';
}
if (oc_appconfig.core.resharingAllowed &&
(possiblePermissions & OC.PERMISSION_SHARE)) {
html += '<input id="canShare-' + escapeHTML(shareWith) +
'" type="checkbox" class="permissions checkbox checkbox--right" name="share" ' +
shareChecked + ' data-permissions="' + OC.PERMISSION_SHARE + '" />';
html += '<label for="canShare-' + escapeHTML(shareWith) + '">' +
t('gallery', 'can share') + '</label>';
}
if (possiblePermissions & OC.PERMISSION_CREATE ||
possiblePermissions & OC.PERMISSION_UPDATE ||
possiblePermissions & OC.PERMISSION_DELETE) {
html += '<input id="canEdit-' + escapeHTML(shareWith) +
'" type="checkbox" class="permissions checkbox checkbox--right" name="edit" ' +
editChecked + ' />';
html += '<label for="canEdit-' + escapeHTML(shareWith) + '">' +
t('gallery', 'can edit') + '</label>';
}
if (shareType !== this.SHARE_TYPE_REMOTE) {
showCrudsButton = '<a href="#" class="showCruds"><img class="svg" alt="' +
t('gallery', 'access control') + '" src="' +
OC.imagePath('core', 'actions/triangle-s') + '"/></a>';
}
html += '<div class="cruds" style="display:none;">';
if (possiblePermissions & OC.PERMISSION_CREATE) {
html += '<input id="canCreate-' + escapeHTML(shareWith) +
'" type="checkbox" class="permissions checkbox checkbox--right" name="create" ' +
createChecked + ' data-permissions="' + OC.PERMISSION_CREATE + '"/>';
html += '<label for="canCreate-' + escapeHTML(shareWith) + '">' +
t('gallery', 'create') + '</label>';
}
if (possiblePermissions & OC.PERMISSION_UPDATE) {
html += '<input id="canUpdate-' + escapeHTML(shareWith) +
'" type="checkbox" class="permissions checkbox checkbox--right" name="update" ' +
updateChecked + ' data-permissions="' + OC.PERMISSION_UPDATE + '"/>';
html += '<label for="canUpdate-' + escapeHTML(shareWith) + '">' +
t('gallery', 'change') + '</label>';
}
if (possiblePermissions & OC.PERMISSION_DELETE) {
html += '<input id="canDelete-' + escapeHTML(shareWith) +
'" type="checkbox" class="permissions checkbox checkbox--right" name="delete" ' +
deleteChecked + ' data-permissions="' + OC.PERMISSION_DELETE + '"/>';
html += '<label for="canDelete-' + escapeHTML(shareWith) + '">' +
t('gallery', 'delete') + '</label>';
}
html += '</div>';
html += '</li>';
html = $(html).appendTo('#dropdown #shareWithList');
if (oc_config.enable_avatars === true) {
if (shareType === this.SHARE_TYPE_USER) {
html.find('.avatar').avatar(escapeHTML(shareWith), 32);
} else {
//Add sharetype to generate different seed if there is a group and use with
// the same name
html.find('.avatar').imageplaceholder(
escapeHTML(shareWith) + ' ' + shareType);
}
}
// insert cruds button into last label element
var lastLabel = html.find('>label:last');
if (lastLabel.exists()) {
lastLabel.append(showCrudsButton);
}
else {
html.find('.cruds').before(showCrudsButton);
}
if (!this.currentShares[shareType]) {
this.currentShares[shareType] = [];
}
this.currentShares[shareType].push(shareItem);
},
/**
* Parses a string to an valid integer (unix timestamp)
* @param time
* @returns {*}
* @internal Only used to work around a bug in the backend
* @private
*/
_parseTime: function (time) {
if (_.isString(time)) {
// skip empty strings and hex values
if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
return null;
}
time = parseInt(time, 10);
if (isNaN(time)) {
time = null;
}
}
return time;
}
};
Gallery.Share = Share;
})(jQuery, Gallery);
$(document).ready(function () {
if (typeof monthNames != 'undefined') {
// min date should always be the next day
var minDate = new Date();
minDate.setDate(minDate.getDate() + 1);
$.datepicker.setDefaults({
monthNames: monthNames,
monthNamesShort: $.map(monthNames, function (v) {
return v.slice(0, 3) + '.';
}),
dayNames: dayNames,
dayNamesMin: $.map(dayNames, function (v) {
return v.slice(0, 2);
}),
dayNamesShort: $.map(dayNames, function (v) {
return v.slice(0, 3) + '.';
}),
firstDay: firstDay,
minDate: minDate
});
}
$(document).on('click', 'a.share', function (event) {
event.stopPropagation();
if ($(this).data('item-type') !== undefined && $(this).data('path') !== undefined) {
var itemType = $(this).data('item-type');
var path = $(this).data('path');
var appendTo = $(this).parents('#controls, #slideshow')[0];
var link = false;
var possiblePermissions = $(this).data('possible-permissions');
if ($(this).data('link') !== undefined && $(this).data('link') == true) {
link = true;
}
if (Gallery.Share.droppedDown) {
if (path != $('#dropdown').data('path')) {
Gallery.Share.hideDropDown(function () {
Gallery.Share.showDropDown(itemType, path, appendTo, link,
possiblePermissions);
});
} else {
Gallery.Share.hideDropDown();
}
} else {
Gallery.Share.showDropDown(itemType, path, appendTo, link, possiblePermissions);
}
}
});
$(this).click(function (event) {
var target = $(event.target);
var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon')
&& !target.closest('#ui-datepicker-div').length &&
!target.closest('.ui-autocomplete').length;
if (Gallery.Share.droppedDown && isMatched &&
$('#dropdown').has(event.target).length === 0) {
Gallery.Share.hideDropDown();
}
});
$(document).on('click', '#dropdown .showCruds', function () {
$(this).closest('li').find('.cruds').toggle();
return false;
});
$(document).on('click', '#dropdown .unshare', function () {
var $li = $(this).closest('li');
var shareType = $li.data('share-type');
var shareWith = $li.attr('data-share-with');
var shareId = $li.data('id');
var $button = $(this);
if (!$button.is('a')) {
$button = $button.closest('a');
}
if ($button.hasClass('icon-loading-small')) {
// deletion in progress
return false;
}
$button.empty().addClass('icon-loading-small');
Gallery.Share.unshare(shareId, function () {
$li.remove();
var index = Gallery.Share.itemShares[shareType].indexOf(shareWith);
Gallery.Share.itemShares[shareType].splice(index, 1);
// updated list of shares
Gallery.Share.currentShares[shareType].splice(index, 1);
});
return false;
});
$(document).on('change', '#dropdown .permissions', function () {
var $li = $(this).closest('li');
var checkboxes = $('.permissions', $li);
if ($(this).attr('name') == 'edit') {
var checked = $(this).is(':checked');
// Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
$(checkboxes).filter('input[name="create"]').attr('checked', checked);
$(checkboxes).filter('input[name="update"]').attr('checked', checked);
$(checkboxes).filter('input[name="delete"]').attr('checked', checked);
} else {
// Uncheck Edit if Create, Update, and Delete are not checked
if (!$(this).is(':checked')
&& !$(checkboxes).filter('input[name="create"]').is(':checked')
&& !$(checkboxes).filter('input[name="update"]').is(':checked')
&& !$(checkboxes).filter('input[name="delete"]').is(':checked')) {
$(checkboxes).filter('input[name="edit"]').attr('checked', false);
// Check Edit if Create, Update, or Delete is checked
} else if (($(this).attr('name') == 'create'
|| $(this).attr('name') == 'update'
|| $(this).attr('name') == 'delete')) {
$(checkboxes).filter('input[name="edit"]').attr('checked', true);
}
}
var permissions = OC.PERMISSION_READ;
$(checkboxes).filter(':not(input[name="edit"])').filter(':checked').each(
function (index, checkbox) {
permissions |= $(checkbox).data('permissions');
});
Gallery.Share.setPermissions($li.data('id'), permissions);
});
$(document).on('change', '#dropdown #linkCheckbox', function () {
var $dropDown = $('#dropdown');
var path = $dropDown.data('item-source');
var shareId = $('#linkCheckbox').data('id');
var shareWith = '';
var publicUpload = 0;
var $loading = $dropDown.find('#link .icon-loading-small');
var $button = $(this);
if (!$loading.hasClass('hidden')) {
// already in progress
return false;
}
if (this.checked) {
// Reset password placeholder
$('#linkPassText').attr('placeholder',
t('gallery', 'Choose a password for the public link'));
// Reset link
$('#linkText').val('');
$('#showPassword').prop('checked', false);
$('#linkPass').hide();
$('#linkSocial').hide();
$('#linkTextMore').hide();
$('#sharingDialogAllowPublicUpload').prop('checked', false);
$('#expirationCheckbox').prop('checked', false);
$('#expirationDate').hide();
var expireDateString = '';
// Create a link
if (oc_appconfig.core.enforcePasswordForPublicLink === false) {
expireDateString = Gallery.Share.getDefaultExpirationDate();
$loading.removeClass('hidden');
$button.addClass('hidden');
$button.prop('disabled', true);
Gallery.Share.share(
path,
Gallery.Share.SHARE_TYPE_LINK,
shareWith,
publicUpload,
null,
OC.PERMISSION_READ,
function (data) {
$loading.addClass('hidden');
$button.removeClass('hidden');
$button.prop('disabled', false);
Gallery.Share.showLink(data.id, data.token, null);
});
} else {
$('#linkPass').slideToggle(OC.menuSpeed);
$('#linkPassText').focus();
}
if (expireDateString !== '') {
Gallery.Share.showExpirationDate(expireDateString);
}
} else {
// Delete private link
Gallery.Share.hideLink();
$('#expiration').slideUp(OC.menuSpeed);
if ($('#linkText').val() !== '') {
$loading.removeClass('hidden');
$button.addClass('hidden');
$button.prop('disabled', true);
Gallery.Share.unshare(shareId, function () {
$loading.addClass('hidden');
$button.removeClass('hidden');
$button.prop('disabled', false);
$('#linkCheckbox').data('id', undefined);
Gallery.Share.itemShares[Gallery.Share.SHARE_TYPE_LINK] = false;
});
}
}
});
$(document).on('click', '#dropdown #linkText', function () {
$(this).focus();
$(this).select();
});
// Handle the Allow Public Upload Checkbox
$(document).on('click', '#sharingDialogAllowPublicUpload', function () {
// Gather data
var $dropDown = $('#dropdown');
var shareId = $('#linkCheckbox').data('id');
var allowPublicUpload = $(this).is(':checked');
var $button = $(this);
var $loading = $dropDown.find('#allowPublicUploadWrapper .icon-loading-small');
if (!$loading.hasClass('hidden')) {
// already in progress
return false;
}
// Update the share information
$button.addClass('hidden');
$button.prop('disabled', true);
$loading.removeClass('hidden');
//(path, shareType, shareWith, publicUpload, password, permissions)
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
publicUpload: allowPublicUpload
}
}).done(function () {
$loading.addClass('hidden');
$button.removeClass('hidden');
$button.prop('disabled', false);
});
});
$(document).on('click', '#dropdown #showPassword', function () {
$('#linkPass').slideToggle(OC.menuSpeed);
if (!$('#showPassword').is(':checked')) {
var shareId = $('#linkCheckbox').data('id');
var $loading = $('#showPassword .icon-loading-small');
$loading.removeClass('hidden');
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
password: null
}
}).done(function () {
$loading.addClass('hidden');
$('#linkPassText').attr('placeholder',
t('gallery', 'Choose a password for the public link'));
});
} else {
$('#linkPassText').focus();
}
});
$(document).on('focusout keyup', '#dropdown #linkPassText', function (event) {
var linkPassText = $('#linkPassText');
if (linkPassText.val() != '' && (event.type == 'focusout' || event.keyCode == 13)) {
var dropDown = $('#dropdown');
var $loading = dropDown.find('#linkPass .icon-loading-small');
var shareId = $('#linkCheckbox').data('id');
$loading.removeClass('hidden');
linkPassText.removeClass('warning');
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
password: $('#linkPassText').val()
}
}).done(function (data) {
$loading.addClass('hidden');
linkPassText.val('');
linkPassText.attr('placeholder', t('gallery', 'Password protected'));
if (oc_appconfig.core.enforcePasswordForPublicLink) {
Gallery.Share.showLink(data.id, data.token, "password set");
}
}).fail(function (xhr) {
var result = xhr.responseJSON;
$loading.addClass('hidden');
linkPassText.val('');
linkPassText.addClass('warning');
linkPassText.attr('placeholder', result.ocs.meta.message);
});
}
});
$(document).on('click', '#dropdown #expirationCheckbox', function () {
if (this.checked) {
Gallery.Share.showExpirationDate('');
} else {
var shareId = $('#linkCheckbox').data('id');
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
expireDate: ''
}
}).done(function () {
$('#expirationDate').slideUp(OC.menuSpeed);
if (oc_appconfig.core.defaultExpireDateEnforced === false) {
$('#defaultExpireMessage').slideDown(OC.menuSpeed);
}
}).fail(function () {
OC.dialogs.alert(t('gallery', 'Error unsetting expiration date'),
t('gallery', 'Error'));
});
}
});
$(document).on('change', '#dropdown #expirationDate', function () {
var shareId = $('#linkCheckbox').data('id');
$(this).tooltip('hide');
$(this).removeClass('error');
$.ajax({
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares/' + shareId +
'?format=json',
type: 'PUT',
data: {
expireDate: $(this).val()
}
}).done(function () {
if (oc_appconfig.core.defaultExpireDateEnforced === 'no') {
$('#defaultExpireMessage').slideUp(OC.menuSpeed);
}
}).fail(function (xhr) {
var result = xhr.responseJSON;
var expirationDateField = $('#dropdown #expirationDate');
if (result && !result.ocs.meta.message) {
expirationDateField.attr('original-title',
t('gallery', 'Error setting expiration date'));
} else {
expirationDateField.attr('original-title', result.ocs.meta.message);
}
expirationDateField.tooltip({placement: 'top'});
expirationDateField.tooltip('show');
expirationDateField.addClass('error');
});
});
$(document).on('submit', '#dropdown #emailPrivateLink', function (event) {
event.preventDefault();
var link = $('#linkText').val();
var itemType = $('#dropdown').data('item-type');
var itemSource = $('#dropdown').data('item-source');
var fileName = $('.last').children()[0].innerText;
var email = $('#email').val();
var expirationDate = '';
if ($('#expirationCheckbox').is(':checked') === true) {
expirationDate = $("#expirationDate").val();
}
if (email != '') {
$('#email').prop('disabled', true);
$('#email').val(t('gallery', 'Sending…'));
$('#emailButton').prop('disabled', true);
$.post(OC.filePath('core', 'ajax', 'share.php'), {
action: 'email',
toaddress: email,
link: link,
file: fileName,
itemType: itemType,
itemSource: itemSource,
expiration: expirationDate
},
function (result) {
$('#email').prop('disabled', false);
$('#emailButton').prop('disabled', false);
if (result && result.status == 'success') {
$('#email').css('font-weight', 'bold').val(t('gallery', 'Email sent'));
setTimeout(function () {
$('#email').css('font-weight', 'normal').val('');
}, 2000);
} else {
OC.dialogs.alert(result.data.message, t('gallery', 'Error while sharing'));
}
});
}
});
$(document).on('click', '#dropdown input[name=mailNotification]', function () {
var $li = $(this).closest('li');
var itemType = $('#dropdown').data('item-type');
var itemSource = $('a.share').data('item-source');
var action = '';
if (this.checked) {
action = 'informRecipients';
} else {
action = 'informRecipientsDisabled';
}
var shareType = $li.data('share-type');
var shareWith = $li.attr('data-share-with');
$.post(OC.filePath('core', 'ajax', 'share.php'), {
action: action,
recipient: shareWith,
shareType: shareType,
itemSource: itemSource,
itemType: itemType
}, function (result) {
if (result.status !== 'success') {
OC.dialogs.alert(t('gallery', result.data.message), t('gallery', 'Warning'));
}
});
});
$(document).on('click', '#dropdown .pop-up', function(event) {
event.preventDefault();
event.stopPropagation();
var url = $(event.currentTarget).data('url');
var newWindow = $(event.currentTarget).data('window');
$(event.currentTarget).tooltip('hide');
if (url) {
if (newWindow === true) {
var width = 600;
var height = 400;
var left = (screen.width / 2) - (width / 2);
var top = (screen.height / 2) - (height / 2);
window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left);
} else {
window.location.href = url;
}
}
});
$(document).on('click', '#dropdown .icon-more', function(event) {
event.preventDefault();
event.stopPropagation();
var children = event.currentTarget.parentNode.children;
$.each(children, function (key, value) {
if (value.classList.contains('popovermenu')) {
$(value).toggle();
}
});
});
});