{"id":5371,"date":"2018-12-19T10:30:26","date_gmt":"2018-12-19T10:30:26","guid":{"rendered":"https:\/\/nextcloud.com\/?p=5371"},"modified":"2023-09-01T11:25:20","modified_gmt":"2023-09-01T09:25:20","slug":"holiday-fun-with-jsloader","status":"publish","type":"post","link":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/","title":{"rendered":"Holiday fun with JSLoader"},"content":{"rendered":"<p>The Nextcloud JSLoader app enables admins to specify JavaScript which loads on each page in Nextcloud. In the spirit of the holidays we decided to use this for a little fun to show you what it can do. Let&#8217;s make it snow in Nextcloud!<\/p>\n<p><img decoding=\"async\" class=\"banner\" src=\"https:\/\/nextcloud.com\/media\/JSLoader-happy-holidays.png\"><\/p>\n<h2>A simple snow script<\/h2>\n<p><a href=\"https:\/\/apps.nextcloud.com\/apps\/jsloader\" rel=\"noopener\" target=\"_blank\">The JSLoader app<\/a> allows the admin to include JavaScript into all Nextcloud pages. This can be used for web analytics for example, but of course &#8211; anything can be loaded and we&#8217;re looking for a wintery theme!<\/p>\n<p>A quick search brought us to this <a class=\"hyperlink\" href=\"http:\/\/www.schillmania.com\/projects\/snowstorm\/\">nice snow script<\/a> from Scott Schiller, who claims the script has been making it snow on the web since 2003. We&#8217;ll simply go ahead and load that script in the JSLoader on Nextcloud, after tuning some of its parameters!<\/p>\n<h3>Step 1<\/h3>\n<p>So first, install the JSLoader app in the app section and visit the admin configuration of this app:<\/p>\n<div class=\"row\">\n<div class=\"col-md-6\"><a href=\"https:\/\/nextcloud.com\/media\/JSloader-app.png\" rel=\"noopener\" target=\"_blank\"><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/nextcloud.com\/media\/JSloader-app.png\" alt=\"\" \/><\/a><\/div>\n<div class=\"col-md-6\"><a href=\"https:\/\/nextcloud.com\/media\/JSLoader-config.png\" rel=\"noopener\" target=\"_blank\"><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/nextcloud.com\/media\/JSLoader-config.png\" alt=\"\" \/><\/a><\/div>\n<\/div>\n<h3>Step 2<\/h3>\n<p>In the javascript input box you paste the following code:<\/p>\n<pre style=\"max-height: 300px;\"><code>\n\/** @license\n * DHTML Snowstorm! JavaScript-based snow for web pages\n * Making it snow on the internets since 2003. You're welcome.\n * -----------------------------------------------------------\n * Version 1.44.20131208 (Previous rev: 1.44.20131125)\n * Copyright (c) 2007, Scott Schiller. All rights reserved.\n * Code provided under the BSD License\n * http:\/\/schillmania.com\/projects\/snowstorm\/license.txt\n *\/\n\/*jslint nomen: true, plusplus: true, sloppy: true, vars: true, white: true *\/\n\/*global window, document, navigator, clearInterval, setInterval *\/\n\nvar snowStorm = (function(window, document) {\n\n  \/\/ --- common properties ---\n\n  this.autoStart = true;          \/\/ Whether the snow should start automatically or not.\n  this.excludeMobile = true;      \/\/ Snow is likely to be bad news for mobile phones' CPUs (and batteries.) Enable at your own risk.\n  this.flakesMax = 128;           \/\/ Limit total amount of snow made (falling + sticking)\n  this.flakesMaxActive = 64;      \/\/ Limit amount of snow falling at once (less = lower CPU use)\n  this.animationInterval = 33;    \/\/ Theoretical &quot;miliseconds per frame&quot; measurement. 20 = fast + smooth, but high CPU use. 50 = more conservative, but slower\n  this.useGPU = true;             \/\/ Enable transform-based hardware acceleration, reduce CPU load.\n  this.className = null;          \/\/ CSS class name for further customization on snow elements\n  this.excludeMobile = true;      \/\/ Snow is likely to be bad news for mobile phones' CPUs (and batteries.) By default, be nice.\n  this.flakeBottom = null;        \/\/ Integer for Y axis snow limit, 0 or null for &quot;full-screen&quot; snow effect\n  this.followMouse = true;        \/\/ Snow movement can respond to the user's mouse\n  this.snowColor = '#efefef';        \/\/ Don't eat (or use?) yellow snow.\n  this.snowCharacter = '\u2744';  \/\/ &amp;bull; = bullet, &amp;middot; is square on some systems etc.\n  this.snowStick = true;          \/\/ Whether or not snow should &quot;stick&quot; at the bottom. When off, will never collect.\n  this.targetElement = null;      \/\/ element which snow will be appended to (null = document.body) - can be an element ID eg. 'myDiv', or a DOM node reference\n  this.useMeltEffect = true;      \/\/ When recycling fallen snow (or rarely, when falling), have it &quot;melt&quot; and fade out if browser supports it\n  this.useTwinkleEffect = true;  \/\/ Allow snow to randomly &quot;flicker&quot; in and out of view while falling\n  this.usePositionFixed = false;  \/\/ true = snow does not shift vertically when scrolling. May increase CPU load, disabled by default - if enabled, used only where supported\n  this.usePixelPosition = false;  \/\/ Whether to use pixel values for snow top\/left vs. percentages. Auto-enabled if body is position:relative or targetElement is specified.\n\n  \/\/ --- less-used bits ---\n\n  this.freezeOnBlur = true;       \/\/ Only snow when the window is in focus (foreground.) Saves CPU.\n  this.flakeLeftOffset = 0;       \/\/ Left margin\/gutter space on edge of container (eg. browser window.) Bump up these values if seeing horizontal scrollbars.\n  this.flakeRightOffset = 0;      \/\/ Right margin\/gutter space on edge of container\n  this.flakeWidth = 32;            \/\/ Max pixel width reserved for snow element\n  this.flakeHeight = 32;           \/\/ Max pixel height reserved for snow element\n  this.vMaxX = 5;                 \/\/ Maximum X velocity range for snow\n  this.vMaxY = 4;                 \/\/ Maximum Y velocity range for snow\n  this.zIndex = 5000;                \/\/ CSS stacking order applied to each snowflake\n\n  \/\/ --- &quot;No user-serviceable parts inside&quot; past this point, yadda yadda ---\n\n  var storm = this,\n  features,\n  \/\/ UA sniffing and backCompat rendering mode checks for fixed position, etc.\n  isIE = navigator.userAgent.match(\/msie\/i),\n  isIE6 = navigator.userAgent.match(\/msie 6\/i),\n  isMobile = navigator.userAgent.match(\/mobile|opera m(ob|in)\/i),\n  isBackCompatIE = (isIE &amp;&amp; document.compatMode === 'BackCompat'),\n  noFixed = (isBackCompatIE || isIE6),\n  screenX = null, screenX2 = null, screenY = null, scrollY = null, docHeight = null, vRndX = null, vRndY = null,\n  windOffset = 1,\n  windMultiplier = 2,\n  flakeTypes = 6,\n  fixedForEverything = false,\n  targetElementIsRelative = false,\n  opacitySupported = (function(){\n    try {\n      document.createElement('div').style.opacity = '0.5';\n    } catch(e) {\n      return false;\n    }\n    return true;\n  }()),\n  didInit = false,\n  docFrag = document.createDocumentFragment();\n\n  features = (function() {\n\n    var getAnimationFrame;\n\n    \/**\n     * hat tip: paul irish\n     * http:\/\/paulirish.com\/2011\/requestanimationframe-for-smart-animating\/\n     * https:\/\/gist.github.com\/838785\n     *\/\n\n    function timeoutShim(callback) {\n      window.setTimeout(callback, 1000\/(storm.animationInterval || 20));\n    }\n\n    var _animationFrame = (window.requestAnimationFrame ||\n        window.webkitRequestAnimationFrame ||\n        window.mozRequestAnimationFrame ||\n        window.oRequestAnimationFrame ||\n        window.msRequestAnimationFrame ||\n        timeoutShim);\n\n    \/\/ apply to window, avoid &quot;illegal invocation&quot; errors in Chrome\n    getAnimationFrame = _animationFrame ? function() {\n      return _animationFrame.apply(window, arguments);\n    } : null;\n\n    var testDiv;\n\n    testDiv = document.createElement('div');\n\n    function has(prop) {\n\n      \/\/ test for feature support\n      var result = testDiv.style[prop];\n      return (result !== undefined ? prop : null);\n\n    }\n\n    \/\/ note local scope.\n    var localFeatures = {\n\n      transform: {\n        ie:  has('-ms-transform'),\n        moz: has('MozTransform'),\n        opera: has('OTransform'),\n        webkit: has('webkitTransform'),\n        w3: has('transform'),\n        prop: null \/\/ the normalized property value\n      },\n\n      getAnimationFrame: getAnimationFrame\n\n    };\n\n    localFeatures.transform.prop = (\n      localFeatures.transform.w3 || \n      localFeatures.transform.moz ||\n      localFeatures.transform.webkit ||\n      localFeatures.transform.ie ||\n      localFeatures.transform.opera\n    );\n\n    testDiv = null;\n\n    return localFeatures;\n\n  }());\n\n  this.timer = null;\n  this.flakes = [];\n  this.disabled = false;\n  this.active = false;\n  this.meltFrameCount = 20;\n  this.meltFrames = [];\n\n  this.setXY = function(o, x, y) {\n\n    if (!o) {\n      return false;\n    }\n\n    if (storm.usePixelPosition || targetElementIsRelative) {\n\n      o.style.left = (x - storm.flakeWidth) + 'px';\n      o.style.top = (y - storm.flakeHeight) + 'px';\n\n    } else if (noFixed) {\n\n      o.style.right = (100-(x\/screenX*100)) + '%';\n      \/\/ avoid creating vertical scrollbars\n      o.style.top = (Math.min(y, docHeight-storm.flakeHeight)) + 'px';\n\n    } else {\n\n      if (!storm.flakeBottom) {\n\n        \/\/ if not using a fixed bottom coordinate...\n        o.style.right = (100-(x\/screenX*100)) + '%';\n        o.style.bottom = (100-(y\/screenY*100)) + '%';\n\n      } else {\n\n        \/\/ absolute top.\n        o.style.right = (100-(x\/screenX*100)) + '%';\n        o.style.top = (Math.min(y, docHeight-storm.flakeHeight)) + 'px';\n\n      }\n\n    }\n\n  };\n\n  this.events = (function() {\n\n    var old = (!window.addEventListener &amp;&amp; window.attachEvent), slice = Array.prototype.slice,\n    evt = {\n      add: (old?'attachEvent':'addEventListener'),\n      remove: (old?'detachEvent':'removeEventListener')\n    };\n\n    function getArgs(oArgs) {\n      var args = slice.call(oArgs), len = args.length;\n      if (old) {\n        args[1] = 'on' + args[1]; \/\/ prefix\n        if (len &gt; 3) {\n          args.pop(); \/\/ no capture\n        }\n      } else if (len === 3) {\n        args.push(false);\n      }\n      return args;\n    }\n\n    function apply(args, sType) {\n      var element = args.shift(),\n          method = [evt[sType]];\n      if (old) {\n        element[method](args[0], args[1]);\n      } else {\n        element[method].apply(element, args);\n      }\n    }\n\n    function addEvent() {\n      apply(getArgs(arguments), 'add');\n    }\n\n    function removeEvent() {\n      apply(getArgs(arguments), 'remove');\n    }\n\n    return {\n      add: addEvent,\n      remove: removeEvent\n    };\n\n  }());\n\n  function rnd(n,min) {\n    if (isNaN(min)) {\n      min = 0;\n    }\n    return (Math.random()*n)+min;\n  }\n\n  function plusMinus(n) {\n    return (parseInt(rnd(2),10)===1?n*-1:n);\n  }\n\n  this.randomizeWind = function() {\n    var i;\n    vRndX = plusMinus(rnd(storm.vMaxX,0.2));\n    vRndY = rnd(storm.vMaxY,0.2);\n    if (this.flakes) {\n      for (i=0; i&lt;this.flakes.length; i++) {\n        if (this.flakes[i].active) {\n          this.flakes[i].setVelocities();\n        }\n      }\n    }\n  };\n\n  this.scrollHandler = function() {\n    var i;\n    \/\/ &quot;attach&quot; snowflakes to bottom of window if no absolute bottom value was given\n    scrollY = (storm.flakeBottom ? 0 : parseInt(window.scrollY || document.documentElement.scrollTop || (noFixed ? document.body.scrollTop : 0), 10));\n    if (isNaN(scrollY)) {\n      scrollY = 0; \/\/ Netscape 6 scroll fix\n    }\n    if (!fixedForEverything &amp;&amp; !storm.flakeBottom &amp;&amp; storm.flakes) {\n      for (i=0; i&lt;storm.flakes.length; i++) {\n        if (storm.flakes[i].active === 0) {\n          storm.flakes[i].stick();\n        }\n      }\n    }\n  };\n\n  this.resizeHandler = function() {\n    if (window.innerWidth || window.innerHeight) {\n      screenX = window.innerWidth - 16 - storm.flakeRightOffset;\n      screenY = (storm.flakeBottom || window.innerHeight);\n    } else {\n      screenX = (document.documentElement.clientWidth || document.body.clientWidth || document.body.scrollWidth) - (!isIE ? 8 : 0) - storm.flakeRightOffset;\n      screenY = storm.flakeBottom || document.documentElement.clientHeight || document.body.clientHeight || document.body.scrollHeight;\n    }\n    docHeight = document.body.offsetHeight;\n    screenX2 = parseInt(screenX\/2,10);\n  };\n\n  this.resizeHandlerAlt = function() {\n    screenX = storm.targetElement.offsetWidth - storm.flakeRightOffset;\n    screenY = storm.flakeBottom || storm.targetElement.offsetHeight;\n    screenX2 = parseInt(screenX\/2,10);\n    docHeight = document.body.offsetHeight;\n  };\n\n  this.freeze = function() {\n    \/\/ pause animation\n    if (!storm.disabled) {\n      storm.disabled = 1;\n    } else {\n      return false;\n    }\n    storm.timer = null;\n  };\n\n  this.resume = function() {\n    if (storm.disabled) {\n       storm.disabled = 0;\n    } else {\n      return false;\n    }\n    storm.timerInit();\n  };\n\n  this.toggleSnow = function() {\n    if (!storm.flakes.length) {\n      \/\/ first run\n      storm.start();\n    } else {\n      storm.active = !storm.active;\n      if (storm.active) {\n        storm.show();\n        storm.resume();\n      } else {\n        storm.stop();\n        storm.freeze();\n      }\n    }\n  };\n\n  this.stop = function() {\n    var i;\n    this.freeze();\n    for (i=0; i&lt;this.flakes.length; i++) {\n      this.flakes[i].o.style.display = 'none';\n    }\n    storm.events.remove(window,'scroll',storm.scrollHandler);\n    storm.events.remove(window,'resize',storm.resizeHandler);\n    if (storm.freezeOnBlur) {\n      if (isIE) {\n        storm.events.remove(document,'focusout',storm.freeze);\n        storm.events.remove(document,'focusin',storm.resume);\n      } else {\n        storm.events.remove(window,'blur',storm.freeze);\n        storm.events.remove(window,'focus',storm.resume);\n      }\n    }\n  };\n\n  this.show = function() {\n    var i;\n    for (i=0; i&lt;this.flakes.length; i++) {\n      this.flakes[i].o.style.display = 'block';\n    }\n  };\n\n  this.SnowFlake = function(type,x,y) {\n    var s = this;\n    this.type = type;\n    this.x = x||parseInt(rnd(screenX-20),10);\n    this.y = (!isNaN(y)?y:-rnd(screenY)-12);\n    this.vX = null;\n    this.vY = null;\n    this.vAmpTypes = [1,1.2,1.4,1.6,1.8]; \/\/ &quot;amplification&quot; for vX\/vY (based on flake size\/type)\n    this.vAmp = this.vAmpTypes[this.type] || 1;\n    this.melting = false;\n    this.meltFrameCount = storm.meltFrameCount;\n    this.meltFrames = storm.meltFrames;\n    this.meltFrame = 0;\n    this.twinkleFrame = 0;\n    this.active = 1;\n    this.fontSize = (10+(this.type\/5)*10);\n    this.o = document.createElement('div');\n    this.o.innerHTML = storm.snowCharacter;\n    if (storm.className) {\n      this.o.setAttribute('class', storm.className);\n    }\n    this.o.style.color = storm.snowColor;\n    this.o.style.position = (fixedForEverything?'fixed':'absolute');\n    if (storm.useGPU &amp;&amp; features.transform.prop) {\n      \/\/ GPU-accelerated snow.\n      this.o.style[features.transform.prop] = 'translate3d(0px, 0px, 0px)';\n    }\n    this.o.style.width = storm.flakeWidth+'px';\n    this.o.style.height = storm.flakeHeight+'px';\n    this.o.style.fontFamily = 'arial,verdana';\n    this.o.style.cursor = 'default';\n    this.o.style.overflow = 'hidden';\n    this.o.style.fontWeight = 'normal';\n    this.o.style.zIndex = storm.zIndex;\n    docFrag.appendChild(this.o);\n\n    this.refresh = function() {\n      if (isNaN(s.x) || isNaN(s.y)) {\n        \/\/ safety check\n        return false;\n      }\n      storm.setXY(s.o, s.x, s.y);\n    };\n\n    this.stick = function() {\n      if (noFixed || (storm.targetElement !== document.documentElement &amp;&amp; storm.targetElement !== document.body)) {\n        s.o.style.top = (screenY+scrollY-storm.flakeHeight)+'px';\n      } else if (storm.flakeBottom) {\n        s.o.style.top = storm.flakeBottom+'px';\n      } else {\n        s.o.style.display = 'none';\n        s.o.style.bottom = '0%';\n        s.o.style.position = 'fixed';\n        s.o.style.display = 'block';\n      }\n    };\n\n    this.vCheck = function() {\n      if (s.vX&gt;=0 &amp;&amp; s.vX&lt;0.2) {\n        s.vX = 0.2;\n      } else if (s.vX&lt;0 &amp;&amp; s.vX&gt;-0.2) {\n        s.vX = -0.2;\n      }\n      if (s.vY&gt;=0 &amp;&amp; s.vY&lt;0.2) {\n        s.vY = 0.2;\n      }\n    };\n\n    this.move = function() {\n      var vX = s.vX*windOffset, yDiff;\n      s.x += vX;\n      s.y += (s.vY*s.vAmp);\n      if (s.x &gt;= screenX || screenX-s.x &lt; storm.flakeWidth) { \/\/ X-axis scroll check\n        s.x = 0;\n      } else if (vX &lt; 0 &amp;&amp; s.x-storm.flakeLeftOffset &lt; -storm.flakeWidth) {\n        s.x = screenX-storm.flakeWidth-1; \/\/ flakeWidth;\n      }\n      s.refresh();\n      yDiff = screenY+scrollY-s.y+storm.flakeHeight;\n      if (yDiff&lt;storm.flakeHeight) {\n        s.active = 0;\n        if (storm.snowStick) {\n          s.stick();\n        } else {\n          s.recycle();\n        }\n      } else {\n        if (storm.useMeltEffect &amp;&amp; s.active &amp;&amp; s.type &lt; 3 &amp;&amp; !s.melting &amp;&amp; Math.random()&gt;0.998) {\n          \/\/ ~1\/1000 chance of melting mid-air, with each frame\n          s.melting = true;\n          s.melt();\n          \/\/ only incrementally melt one frame\n          \/\/ s.melting = false;\n        }\n        if (storm.useTwinkleEffect) {\n          if (s.twinkleFrame &lt; 0) {\n            if (Math.random() &gt; 0.97) {\n              s.twinkleFrame = parseInt(Math.random() * 8, 10);\n            }\n          } else {\n            s.twinkleFrame--;\n            if (!opacitySupported) {\n              s.o.style.visibility = (s.twinkleFrame &amp;&amp; s.twinkleFrame % 2 === 0 ? 'hidden' : 'visible');\n            } else {\n              s.o.style.opacity = (s.twinkleFrame &amp;&amp; s.twinkleFrame % 2 === 0 ? 0 : 1);\n            }\n          }\n        }\n      }\n    };\n\n    this.animate = function() {\n      \/\/ main animation loop\n      \/\/ move, check status, die etc.\n      s.move();\n    };\n\n    this.setVelocities = function() {\n      s.vX = vRndX+rnd(storm.vMaxX*0.12,0.1);\n      s.vY = vRndY+rnd(storm.vMaxY*0.12,0.1);\n    };\n\n    this.setOpacity = function(o,opacity) {\n      if (!opacitySupported) {\n        return false;\n      }\n      o.style.opacity = opacity;\n    };\n\n    this.melt = function() {\n      if (!storm.useMeltEffect || !s.melting) {\n        s.recycle();\n      } else {\n        if (s.meltFrame &lt; s.meltFrameCount) {\n          s.setOpacity(s.o,s.meltFrames[s.meltFrame]);\n          s.o.style.fontSize = s.fontSize-(s.fontSize*(s.meltFrame\/s.meltFrameCount))+'px';\n          s.o.style.lineHeight = storm.flakeHeight+2+(storm.flakeHeight*0.75*(s.meltFrame\/s.meltFrameCount))+'px';\n          s.meltFrame++;\n        } else {\n          s.recycle();\n        }\n      }\n    };\n\n    this.recycle = function() {\n      s.o.style.display = 'none';\n      s.o.style.position = (fixedForEverything?'fixed':'absolute');\n      s.o.style.bottom = 'auto';\n      s.setVelocities();\n      s.vCheck();\n      s.meltFrame = 0;\n      s.melting = false;\n      s.setOpacity(s.o,1);\n      s.o.style.padding = '0px';\n      s.o.style.margin = '0px';\n      s.o.style.fontSize = s.fontSize+'px';\n      s.o.style.lineHeight = (storm.flakeHeight+2)+'px';\n      s.o.style.textAlign = 'center';\n      s.o.style.verticalAlign = 'baseline';\n      s.x = parseInt(rnd(screenX-storm.flakeWidth-20),10);\n      s.y = parseInt(rnd(screenY)*-1,10)-storm.flakeHeight;\n      s.refresh();\n      s.o.style.display = 'block';\n      s.active = 1;\n    };\n\n    this.recycle(); \/\/ set up x\/y coords etc.\n    this.refresh();\n\n  };\n\n  this.snow = function() {\n    var active = 0, flake = null, i, j;\n    for (i=0, j=storm.flakes.length; i&lt;j; i++) {\n      if (storm.flakes[i].active === 1) {\n        storm.flakes[i].move();\n        active++;\n      }\n      if (storm.flakes[i].melting) {\n        storm.flakes[i].melt();\n      }\n    }\n    if (active&lt;storm.flakesMaxActive) {\n      flake = storm.flakes[parseInt(rnd(storm.flakes.length),10)];\n      if (flake.active === 0) {\n        flake.melting = true;\n      }\n    }\n    if (storm.timer) {\n      features.getAnimationFrame(storm.snow);\n    }\n  };\n\n  this.mouseMove = function(e) {\n    if (!storm.followMouse) {\n      return true;\n    }\n    var x = parseInt(e.clientX,10);\n    if (x&lt;screenX2) {\n      windOffset = -windMultiplier+(x\/screenX2*windMultiplier);\n    } else {\n      x -= screenX2;\n      windOffset = (x\/screenX2)*windMultiplier;\n    }\n  };\n\n  this.createSnow = function(limit,allowInactive) {\n    var i;\n    for (i=0; i&lt;limit; i++) {\n      storm.flakes[storm.flakes.length] = new storm.SnowFlake(parseInt(rnd(flakeTypes),10));\n      if (allowInactive || i&gt;storm.flakesMaxActive) {\n        storm.flakes[storm.flakes.length-1].active = -1;\n      }\n    }\n    storm.targetElement.appendChild(docFrag);\n  };\n\n  this.timerInit = function() {\n    storm.timer = true;\n    storm.snow();\n  };\n\n  this.init = function() {\n    var i;\n    for (i=0; i&lt;storm.meltFrameCount; i++) {\n      storm.meltFrames.push(1-(i\/storm.meltFrameCount));\n    }\n    storm.randomizeWind();\n    storm.createSnow(storm.flakesMax); \/\/ create initial batch\n    storm.events.add(window,'resize',storm.resizeHandler);\n    storm.events.add(window,'scroll',storm.scrollHandler);\n    if (storm.freezeOnBlur) {\n      if (isIE) {\n        storm.events.add(document,'focusout',storm.freeze);\n        storm.events.add(document,'focusin',storm.resume);\n      } else {\n        storm.events.add(window,'blur',storm.freeze);\n        storm.events.add(window,'focus',storm.resume);\n      }\n    }\n    storm.resizeHandler();\n    storm.scrollHandler();\n    if (storm.followMouse) {\n      storm.events.add(isIE?document:window,'mousemove',storm.mouseMove);\n    }\n    storm.animationInterval = Math.max(20,storm.animationInterval);\n    storm.timerInit();\n  };\n\n  this.start = function(bFromOnLoad) {\n    if (!didInit) {\n      didInit = true;\n    } else if (bFromOnLoad) {\n      \/\/ already loaded and running\n      return true;\n    }\n    if (typeof storm.targetElement === 'string') {\n      var targetID = storm.targetElement;\n      storm.targetElement = document.getElementById(targetID);\n      if (!storm.targetElement) {\n        throw new Error('Snowstorm: Unable to get targetElement &quot;'+targetID+'&quot;');\n      }\n    }\n    if (!storm.targetElement) {\n      storm.targetElement = (document.body || document.documentElement);\n    }\n    if (storm.targetElement !== document.documentElement &amp;&amp; storm.targetElement !== document.body) {\n      \/\/ re-map handler to get element instead of screen dimensions\n      storm.resizeHandler = storm.resizeHandlerAlt;\n      \/\/and force-enable pixel positioning\n      storm.usePixelPosition = true;\n    }\n    storm.resizeHandler(); \/\/ get bounding box elements\n    storm.usePositionFixed = (storm.usePositionFixed &amp;&amp; !noFixed &amp;&amp; !storm.flakeBottom); \/\/ whether or not position:fixed is to be used\n    if (window.getComputedStyle) {\n      \/\/ attempt to determine if body or user-specified snow parent element is relatlively-positioned.\n      try {\n        targetElementIsRelative = (window.getComputedStyle(storm.targetElement, null).getPropertyValue('position') === 'relative');\n      } catch(e) {\n        \/\/ oh well\n        targetElementIsRelative = false;\n      }\n    }\n    fixedForEverything = storm.usePositionFixed;\n    if (screenX &amp;&amp; screenY &amp;&amp; !storm.disabled) {\n      storm.init();\n      storm.active = true;\n    }\n  };\n\n  function doDelayedStart() {\n    window.setTimeout(function() {\n      storm.start(true);\n    }, 20);\n    \/\/ event cleanup\n    storm.events.remove(isIE?document:window,'mousemove',doDelayedStart);\n  }\n\n  function doStart() {\n    if (!storm.excludeMobile || !isMobile) {\n      doDelayedStart();\n    }\n    \/\/ event cleanup\n    storm.events.remove(window, 'load', doStart);\n  }\n\n  \/\/ hooks for starting the snow\n  if (storm.autoStart) {\n    storm.events.add(window, 'load', doStart, false);\n  }\n\n  return this;\n\n}(window, document));\n<\/code><\/pre>\n<h3>Step 3<\/h3>\n<p>After pasting, click outside the box to let Nextcloud fill in the CSP text and click save.<\/p>\n<p>Now, open a new window in anonymous browsing mode and check the result! You can of course modify the whole design of your Nextcloud easily using the built-in theming&#8230;<\/p>\n<div class=\"row\">\n<div class=\"col-md-6\"><a href=\"https:\/\/nextcloud.com\/media\/JSLoader-result.png\" rel=\"noopener\" target=\"_blank\"><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/nextcloud.com\/media\/JSLoader-result.png\" alt=\"\" \/><\/a><\/div>\n<div class=\"col-md-6\"><a href=\"https:\/\/nextcloud.com\/media\/JSLoader-result-2.png\" rel=\"noopener\" target=\"_blank\"><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/nextcloud.com\/media\/JSLoader-result-2.png\" alt=\"\" \/><\/a><\/div>\n<\/div>\n<h2>Tuning<\/h2>\n<p>There are a few parameters to tune here:<\/p>\n<ul>\n<li>We replaced the default dot with a snow emoji, setting <code>this.snowCharacter = '\u2744';<\/code><\/li>\n<li>Increased the size of flakes to max 32&#215;32 pixels (<code>flakeWidth<\/code> and <code>flakeHeight<\/code>)<\/li>\n<li>Setting <code>this.snowColor = '#efefef';<\/code> will make the snow a little more visible on the largely white background of Nextcloud.<\/li>\n<li>To make sure the snow shows up over the UI within Nextcloud, we set <code>this.zIndex = 5000;<\/code> &#8211; if you don&#8217;t modify this, you won&#8217;t see snow during normal Nextcloud use, only on the login screen.<br \/>\nVarious other values can ensure the snow does or doesn&#8217;t go over certain items. For example, with a value of <code>1000<\/code> it won&#8217;t cover the top bar, but only the content in the window.<\/li>\n<\/ul>\n<h2>Ending the year<\/h2>\n<p>We&#8217;re now nearing the end of the year for most of us. We released Nextcloud 15 and activities are winding down! For those coming to the 35th installment of the Chaos Communication Congress in Germany in the days before December 31st, <a class=\"hyperlink\" href=\"https:\/\/nextcloud.com\/blog\/attending-35c3-fosdem-scale-meet-nextcloud\/\">we&#8217;ll be there<\/a> with an assembly and several talks and workshops! If you can&#8217;t make it there, we hope to see you next year at enterprise events like <a class=\"hyperlink\" href=\"https:\/\/nextcloud.com\/blog\/meet-nextcloud-experts-at-enterprise-events-early-2019\/\">CS3, the Univention summit or Didacta<\/a>, or community events like <a class=\"hyperlink\" href=\"https:\/\/nextcloud.com\/blog\/attending-35c3-fosdem-scale-meet-nextcloud\/\">FOSDEM or SCALE<\/a> in the first quarter of the year. Then there are of course our <a class=\"hyperlink\" href=\"https:\/\/nextcloud.com\/training\">Enterprise Day in March<\/a> or the Nextcloud Conference 2019 (date to be announced), perfect opportunities to dive deeper in Nextcloud.<\/p>\n<h2>We wish you all happy holidays and a great new year!<\/h2>\n","protected":false},"excerpt":{"rendered":"<p>The Nextcloud JSLoader app enables admins to specify JavaScript which loads on each page in Nextcloud. In the spirit of the holidays we decided to use this for a little fun to show you what it can do. Let&#8217;s make it snow in Nextcloud! A simple snow script The JSLoader app allows the admin to [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":5372,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"publish_to_discourse":"0","publish_post_category":"22","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"128549","discourse_permalink":"https:\/\/help.nextcloud.com\/t\/holiday-fun-with-jsloader\/43566","wpdc_publishing_response":"success","wpdc_publishing_error":"","footnotes":""},"categories":[6],"tags":[],"class_list":["post-5371","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-community"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Holiday fun with JSLoader - Nextcloud<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Holiday fun with JSLoader - Nextcloud\" \/>\n<meta property=\"og:description\" content=\"The Nextcloud JSLoader app enables admins to specify JavaScript which loads on each page in Nextcloud. In the spirit of the holidays we decided to use this for a little fun to show you what it can do. Let&#8217;s make it snow in Nextcloud! A simple snow script The JSLoader app allows the admin to [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\" \/>\n<meta property=\"og:site_name\" content=\"Nextcloud\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Nextclouders\/\" \/>\n<meta property=\"article:published_time\" content=\"2018-12-19T10:30:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-01T09:25:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/nextcloud.com\/wp-content\/uploads\/2018\/12\/JSLoader-happy-holidays.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1599\" \/>\n\t<meta property=\"og:image:height\" content=\"900\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Jos Poortvliet\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@nextclouders\" \/>\n<meta name=\"twitter:site\" content=\"@nextclouders\" \/>\n<meta name=\"twitter:label1\" content=\"Scritto da\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jos Poortvliet\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\"},\"author\":{\"name\":\"Jos Poortvliet\",\"@id\":\"https:\/\/nextcloud.com\/it\/#\/schema\/person\/c3c1a52fbc1c6a2be2f1df8818daa8c2\"},\"headline\":\"Holiday fun with JSLoader\",\"datePublished\":\"2018-12-19T10:30:26+00:00\",\"dateModified\":\"2023-09-01T09:25:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\"},\"wordCount\":459,\"publisher\":{\"@id\":\"https:\/\/nextcloud.com\/it\/#organization\"},\"image\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png\",\"articleSection\":[\"Community\"],\"inLanguage\":\"it-IT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\",\"url\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\",\"name\":\"Holiday fun with JSLoader - Nextcloud\",\"isPartOf\":{\"@id\":\"https:\/\/nextcloud.com\/it\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png\",\"datePublished\":\"2018-12-19T10:30:26+00:00\",\"dateModified\":\"2023-09-01T09:25:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage\",\"url\":\"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png\",\"contentUrl\":\"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png\",\"width\":1599,\"height\":900},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/nextcloud.com\/it\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Holiday fun with JSLoader\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/nextcloud.com\/it\/#website\",\"url\":\"https:\/\/nextcloud.com\/it\/\",\"name\":\"Nextcloud\",\"description\":\"Regain control over your data\",\"publisher\":{\"@id\":\"https:\/\/nextcloud.com\/it\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/nextcloud.com\/it\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/nextcloud.com\/it\/#organization\",\"name\":\"Nextcloud\",\"url\":\"https:\/\/nextcloud.com\/it\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/nextcloud.com\/it\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/nextcloud.com\/c\/uploads\/2022\/10\/nextcloud-logo-blue-transparent.svg\",\"contentUrl\":\"https:\/\/nextcloud.com\/c\/uploads\/2022\/10\/nextcloud-logo-blue-transparent.svg\",\"width\":\"1024\",\"height\":\"1024\",\"caption\":\"Nextcloud\"},\"image\":{\"@id\":\"https:\/\/nextcloud.com\/it\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Nextclouders\/\",\"https:\/\/x.com\/nextclouders\",\"https:\/\/www.linkedin.com\/company\/10827569\/\",\"https:\/\/youtube.com\/nextcloud\",\"https:\/\/www.instagram.com\/nextclouders\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/nextcloud.com\/it\/#\/schema\/person\/c3c1a52fbc1c6a2be2f1df8818daa8c2\",\"name\":\"Jos Poortvliet\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\/\/nextcloud.com\/it\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/1c6de64891ee8c4902e1ac198f26ea5fb202d8bbaa7bce35b6a184610bbe4a3d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/1c6de64891ee8c4902e1ac198f26ea5fb202d8bbaa7bce35b6a184610bbe4a3d?s=96&d=mm&r=g\",\"caption\":\"Jos Poortvliet\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Holiday fun with JSLoader - Nextcloud","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/","og_locale":"it_IT","og_type":"article","og_title":"Holiday fun with JSLoader - Nextcloud","og_description":"The Nextcloud JSLoader app enables admins to specify JavaScript which loads on each page in Nextcloud. In the spirit of the holidays we decided to use this for a little fun to show you what it can do. Let&#8217;s make it snow in Nextcloud! A simple snow script The JSLoader app allows the admin to [&hellip;]","og_url":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/","og_site_name":"Nextcloud","article_publisher":"https:\/\/www.facebook.com\/Nextclouders\/","article_published_time":"2018-12-19T10:30:26+00:00","article_modified_time":"2023-09-01T09:25:20+00:00","og_image":[{"width":1599,"height":900,"url":"https:\/\/nextcloud.com\/wp-content\/uploads\/2018\/12\/JSLoader-happy-holidays.png","type":"image\/png"}],"author":"Jos Poortvliet","twitter_card":"summary_large_image","twitter_creator":"@nextclouders","twitter_site":"@nextclouders","twitter_misc":{"Scritto da":"Jos Poortvliet","Tempo di lettura stimato":"14 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#article","isPartOf":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/"},"author":{"name":"Jos Poortvliet","@id":"https:\/\/nextcloud.com\/it\/#\/schema\/person\/c3c1a52fbc1c6a2be2f1df8818daa8c2"},"headline":"Holiday fun with JSLoader","datePublished":"2018-12-19T10:30:26+00:00","dateModified":"2023-09-01T09:25:20+00:00","mainEntityOfPage":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/"},"wordCount":459,"publisher":{"@id":"https:\/\/nextcloud.com\/it\/#organization"},"image":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage"},"thumbnailUrl":"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png","articleSection":["Community"],"inLanguage":"it-IT"},{"@type":"WebPage","@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/","url":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/","name":"Holiday fun with JSLoader - Nextcloud","isPartOf":{"@id":"https:\/\/nextcloud.com\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage"},"image":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage"},"thumbnailUrl":"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png","datePublished":"2018-12-19T10:30:26+00:00","dateModified":"2023-09-01T09:25:20+00:00","breadcrumb":{"@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#primaryimage","url":"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png","contentUrl":"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png","width":1599,"height":900},{"@type":"BreadcrumbList","@id":"https:\/\/nextcloud.com\/it\/blog\/holiday-fun-with-jsloader\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/nextcloud.com\/it\/"},{"@type":"ListItem","position":2,"name":"Holiday fun with JSLoader"}]},{"@type":"WebSite","@id":"https:\/\/nextcloud.com\/it\/#website","url":"https:\/\/nextcloud.com\/it\/","name":"Nextcloud","description":"Regain control over your data","publisher":{"@id":"https:\/\/nextcloud.com\/it\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/nextcloud.com\/it\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Organization","@id":"https:\/\/nextcloud.com\/it\/#organization","name":"Nextcloud","url":"https:\/\/nextcloud.com\/it\/","logo":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/nextcloud.com\/it\/#\/schema\/logo\/image\/","url":"https:\/\/nextcloud.com\/c\/uploads\/2022\/10\/nextcloud-logo-blue-transparent.svg","contentUrl":"https:\/\/nextcloud.com\/c\/uploads\/2022\/10\/nextcloud-logo-blue-transparent.svg","width":"1024","height":"1024","caption":"Nextcloud"},"image":{"@id":"https:\/\/nextcloud.com\/it\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Nextclouders\/","https:\/\/x.com\/nextclouders","https:\/\/www.linkedin.com\/company\/10827569\/","https:\/\/youtube.com\/nextcloud","https:\/\/www.instagram.com\/nextclouders\/"]},{"@type":"Person","@id":"https:\/\/nextcloud.com\/it\/#\/schema\/person\/c3c1a52fbc1c6a2be2f1df8818daa8c2","name":"Jos Poortvliet","image":{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/nextcloud.com\/it\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/1c6de64891ee8c4902e1ac198f26ea5fb202d8bbaa7bce35b6a184610bbe4a3d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1c6de64891ee8c4902e1ac198f26ea5fb202d8bbaa7bce35b6a184610bbe4a3d?s=96&d=mm&r=g","caption":"Jos Poortvliet"}}]}},"featured_media_url":"https:\/\/nextcloud.com\/c\/uploads\/2018\/12\/JSLoader-happy-holidays.png","_links":{"self":[{"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/posts\/5371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/comments?post=5371"}],"version-history":[{"count":0,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/posts\/5371\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/media\/5372"}],"wp:attachment":[{"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/media?parent=5371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/categories?post=5371"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nextcloud.com\/it\/wp-json\/wp\/v2\/tags?post=5371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}