Nice little directory browser :D
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Header: <marquee> is, er, not perfect...

uses a slightly modified version of aamirafridi/jQuery.Marquee ... ideally no more extra js but w/e...

+512 -1
+12 -1
Components/Header.razor
··· 43 43 <hr/> 44 44 <div id="message" class="text-center items-center text-3xl flex flex-row gap-3"> 45 45 <div>🚧</div> 46 - <marquee scrollamount="10" class="text-sm">Hello alpha tester! I am testing out a rewrite of this file browser, sorry if anything breaks! If you need anything, please don't hesitate to contact me on Twitter.</marquee> 46 + <span class="mask-[linear-gradient(to_right,transparent,black_5%,black_95%,transparent)] w-full overflow-hidden text-sm invisible" 47 + data-duration="7500" 48 + data-gap="25" 49 + data-duplicated="true" 50 + data-duplicateCount="1" 51 + _="on load call $(me).marquee() then remove .invisible" 52 + > 53 + <div class="flex gap-[25px]"> 54 + <span>Hello, alpha tester! I'm testing out a rewrite of this file browser, sorry if anything breaks! If you need anything, please don't hesitate to contact me on Twitter!</span> 55 + <span>🦋</span> @* divider charm *@ 56 + </div> 57 + </span> 47 58 <div>🚧</div> 48 59 </div> 49 60 </header>
+1
Components/Layout/MainLayout.razor
··· 28 28 29 29 <script src="/.nhnd/htmx.js"></script> 30 30 <script src="/.nhnd/jquery.js"></script> 31 + <script src="/.nhnd/jquery.marquee.js"></script> 31 32 <meta name="htmx-config" content='{"scrollIntoViewOnBoost":false}' /> 32 33 33 34 <meta name="darkreader-lock" />
+487
public/jquery.marquee.js
··· 1 + /** 2 + * jQuery.marquee - scrolling text like old marquee element 3 + * @author Aamir Afridi - aamirafridi(at)gmail(dot)com / http://aamirafridi.com/jquery/jquery-marquee-plugin 4 + */ 5 + ;(function(factory) { 6 + 'use strict'; 7 + if (typeof define === 'function' && define.amd) { 8 + define(['jquery'], factory); 9 + } else if (typeof exports !== 'undefined') { 10 + module.exports = factory(require('jquery')); 11 + } else { 12 + factory(jQuery); 13 + } 14 + })(function($) { 15 + $.fn.marquee = function(options) { 16 + return this.each(function() { 17 + // Extend the options if any provided 18 + var o = $.extend({}, $.fn.marquee.defaults, options), 19 + $this = $(this), 20 + $marqueeWrapper, containerWidth, animationCss, verticalDir, elWidth, 21 + loopCount = 3, 22 + playState = 'animation-play-state', 23 + css3AnimationIsSupported = false, 24 + 25 + // Private methods 26 + _prefixedEvent = function(element, type, callback) { 27 + var pfx = ["webkit", "moz", "MS", "o", ""]; 28 + for (var p = 0; p < pfx.length; p++) { 29 + if (!pfx[p]) type = type.toLowerCase(); 30 + element.addEventListener(pfx[p] + type, callback, false); 31 + } 32 + }, 33 + 34 + _objToString = function(obj) { 35 + var tabjson = []; 36 + for (var p in obj) { 37 + if (obj.hasOwnProperty(p)) { 38 + tabjson.push(p + ':' + obj[p]); 39 + } 40 + } 41 + tabjson.push(); 42 + return '{' + tabjson.join(',') + '}'; 43 + }, 44 + 45 + _startAnimationWithDelay = function() { 46 + $this.timer = setTimeout(animate, o.delayBeforeStart); 47 + }, 48 + 49 + // Public methods 50 + methods = { 51 + pause: function() { 52 + $marqueeWrapper.css(playState, 'paused'); 53 + // save the status 54 + $this.data('runningStatus', 'paused'); 55 + // fire event 56 + $this.trigger('paused'); 57 + }, 58 + 59 + resume: function() { 60 + // resume using css3 61 + $marqueeWrapper.css(playState, 'running'); 62 + // save the status 63 + $this.data('runningStatus', 'resumed'); 64 + // fire event 65 + $this.trigger('resumed'); 66 + }, 67 + 68 + toggle: function() { 69 + methods[$this.data('runningStatus') === 'resumed' ? 'pause' : 'resume'](); 70 + }, 71 + 72 + destroy: function() { 73 + // Clear timer 74 + clearTimeout($this.timer); 75 + // Unbind all events 76 + $this.find("*").addBack().off(); 77 + // Just unwrap the elements that has been added using this plugin 78 + $this.html($this.find('.js-marquee:first').html()); 79 + } 80 + }; 81 + 82 + // Check for methods 83 + if (typeof options === 'string') { 84 + if ($.isFunction(methods[options])) { 85 + // Following two IF statements to support public methods 86 + if (!$marqueeWrapper) { 87 + $marqueeWrapper = $this.find('.js-marquee-wrapper'); 88 + } 89 + if ($this.data('css3AnimationIsSupported') === true) { 90 + css3AnimationIsSupported = true; 91 + } 92 + methods[options](); 93 + } 94 + return; 95 + } 96 + 97 + /* Check if element has data attributes. They have top priority 98 + For details https://twitter.com/aamirafridi/status/403848044069679104 - Can't find a better solution :/ 99 + jQuery 1.3.2 doesn't support $.data().KEY hence writting the following */ 100 + var dataAttributes = {}, 101 + attr; 102 + $.each(o, function(key) { 103 + // Check if element has this data attribute 104 + attr = $this.attr('data-' + key); 105 + if (typeof attr !== 'undefined') { 106 + // Now check if value is boolean or not 107 + switch (attr) { 108 + case 'true': 109 + attr = true; 110 + break; 111 + case 'false': 112 + attr = false; 113 + break; 114 + } 115 + o[key] = attr; 116 + } 117 + }); 118 + 119 + // Reintroduce speed as an option. It calculates duration as a factor of the container width 120 + // measured in pixels per second. 121 + if (o.speed) { 122 + o.duration = parseInt($this.width(), 10) / o.speed * 1000; 123 + } 124 + 125 + // Shortcut to see if direction is upward or downward 126 + verticalDir = o.direction === 'up' || o.direction === 'down'; 127 + 128 + // no gap if not duplicated 129 + o.gap = o.duplicated ? parseInt(o.gap) : 0; 130 + 131 + // wrap inner content into a div 132 + $this.wrapInner('<div class="js-marquee"></div>'); 133 + 134 + // Make copy of the element 135 + var $el = $this.find('.js-marquee').css({ 136 + 'margin-right': o.gap, 137 + 'float': 'left' 138 + }); 139 + 140 + if (o.duplicated) { 141 + if (o.duplicateCount <= 0) { 142 + // If duplication enabled then the duplicate count must be a positive number 143 + o.duplicateCount = 1; 144 + } 145 + for (let duplicateLoop = 0; duplicateLoop < o.duplicateCount; duplicateLoop++) { 146 + $el.clone(true).appendTo($this); 147 + } 148 + } 149 + 150 + // wrap both inner elements into one div 151 + $this.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>'); 152 + 153 + // Save the reference of the wrapper 154 + $marqueeWrapper = $this.find('.js-marquee-wrapper'); 155 + 156 + // If direction is up or down, get the height of main element 157 + if (verticalDir) { 158 + var containerHeight = $this.height(); 159 + $marqueeWrapper.removeAttr('style'); 160 + $this.height(containerHeight); 161 + 162 + // Change the CSS for js-marquee element 163 + $this.find('.js-marquee').css({ 164 + 'float': 'none', 165 + 'margin-bottom': o.gap, 166 + 'margin-right': 0 167 + }); 168 + 169 + // Remove bottom margin from 2nd element if duplicated 170 + if (o.duplicated) { 171 + $this.find('.js-marquee:last').css({ 172 + 'margin-bottom': 0 173 + }); 174 + } 175 + 176 + var elHeight = $this.find('.js-marquee:first').height() + o.gap; 177 + 178 + // adjust the animation duration according to the text length 179 + if (o.startVisible && !o.duplicated) { 180 + // Compute the complete animation duration and save it for later reference 181 + // formula is to: (Height of the text node + height of the main container / Height of the main container) * duration; 182 + o._completeDuration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration; 183 + 184 + // formula is to: (Height of the text node / height of the main container) * duration 185 + o.duration = (parseInt(elHeight, 10) / parseInt(containerHeight, 10)) * o.duration; 186 + } else { 187 + // formula is to: (Height of the text node + height of the main container / Height of the main container) * duration; 188 + o.duration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration; 189 + } 190 + 191 + } else { 192 + // Save the width of the each element so we can use it in animation 193 + elWidth = $this.find('.js-marquee:first').width() + o.gap; 194 + 195 + // container width 196 + containerWidth = $this.width(); 197 + 198 + // adjust the animation duration according to the text length 199 + if (o.startVisible && !o.duplicated) { 200 + // Compute the complete animation duration and save it for later reference 201 + // formula is to: (Width of the text node + width of the main container / Width of the main container) * duration; 202 + o._completeDuration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration; 203 + 204 + // (Width of the text node / width of the main container) * duration 205 + o.duration = (parseInt(elWidth, 10) / parseInt(containerWidth, 10)) * o.duration; 206 + } else { 207 + // formula is to: (Width of the text node + width of the main container / Width of the main container) * duration; 208 + o.duration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration; 209 + } 210 + } 211 + 212 + // if duplicated then reduce the duration 213 + if (o.duplicated) { 214 + o.duration = o.duration / 2; 215 + } 216 + 217 + if (o.allowCss3Support) { 218 + var elm = document.body || document.createElement('div'), 219 + animationName = 'marqueeAnimation-' + Math.floor(Math.random() * 10000000), 220 + domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), 221 + animationString = 'animation', 222 + animationCss3Str = '', 223 + keyframeString = ''; 224 + 225 + // Check css3 support 226 + if (elm.style.animation !== undefined) { 227 + keyframeString = '@keyframes ' + animationName + ' '; 228 + css3AnimationIsSupported = true; 229 + } 230 + 231 + if (css3AnimationIsSupported === false) { 232 + for (var i = 0; i < domPrefixes.length; i++) { 233 + if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) { 234 + var prefix = '-' + domPrefixes[i].toLowerCase() + '-'; 235 + animationString = prefix + animationString; 236 + playState = prefix + playState; 237 + keyframeString = '@' + prefix + 'keyframes ' + animationName + ' '; 238 + css3AnimationIsSupported = true; 239 + break; 240 + } 241 + } 242 + } 243 + 244 + if (css3AnimationIsSupported) { 245 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's infinite linear'; 246 + $this.data('css3AnimationIsSupported', true); 247 + } 248 + } 249 + 250 + var _rePositionVertically = function() { 251 + $marqueeWrapper.css('transform', 'translateY(' + (o.direction === 'up' ? containerHeight + 'px' : '-' + elHeight + 'px') + ')'); 252 + }, 253 + _rePositionHorizontally = function() { 254 + $marqueeWrapper.css('transform', 'translateX(' + (o.direction === 'left' ? containerWidth + 'px' : '-' + elWidth + 'px') + ')'); 255 + }; 256 + 257 + // if duplicated option is set to true than position the wrapper 258 + if (o.duplicated) { 259 + if (verticalDir) { 260 + if (o.startVisible) { 261 + $marqueeWrapper.css('transform', 'translateY(0)'); 262 + } else { 263 + $marqueeWrapper.css('transform', 'translateY(' + (o.direction === 'up' ? containerHeight + 'px' : '-' + ((elHeight * 2) - o.gap) + 'px') + ')'); 264 + } 265 + } else { 266 + if (o.startVisible) { 267 + $marqueeWrapper.css('transform', 'translateX(0)'); 268 + } else { 269 + $marqueeWrapper.css('transform', 'translateX(' + (o.direction === 'left' ? containerWidth + 'px' : '-' + ((elWidth * 2) - o.gap) + 'px') + ')'); 270 + } 271 + } 272 + 273 + // If the text starts out visible we can skip the two initial loops 274 + if (!o.startVisible) { 275 + loopCount = 1; 276 + } 277 + } else if (o.startVisible) { 278 + // We only have two different loops if marquee is duplicated and starts visible 279 + loopCount = 2; 280 + } else { 281 + if (verticalDir) { 282 + _rePositionVertically(); 283 + } else { 284 + _rePositionHorizontally(); 285 + } 286 + } 287 + 288 + // Animate recursive method 289 + var animate = function() { 290 + if (o.duplicated) { 291 + // When duplicated, the first loop will be scroll longer so double the duration 292 + if (loopCount === 1) { 293 + o._originalDuration = o.duration; 294 + if (verticalDir) { 295 + o.duration = o.direction === 'up' ? o.duration + (containerHeight / ((elHeight) / o.duration)) : o.duration * 2; 296 + } else { 297 + o.duration = o.direction === 'left' ? o.duration + (containerWidth / ((elWidth) / o.duration)) : o.duration * 2; 298 + } 299 + // Adjust the css3 animation as well 300 + if (animationCss3Str) { 301 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's linear'; 302 + } 303 + loopCount++; 304 + } 305 + // On 2nd loop things back to normal, normal duration for the rest of animations 306 + else if (loopCount === 2) { 307 + o.duration = o._originalDuration; 308 + // Adjust the css3 animation as well 309 + if (animationCss3Str) { 310 + animationName = animationName + '0'; 311 + keyframeString = $.trim(keyframeString) + '0 '; 312 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite linear'; 313 + } 314 + loopCount++; 315 + } 316 + } 317 + 318 + if (verticalDir) { 319 + if (o.duplicated) { 320 + 321 + // Adjust the starting point of animation only when first loops finishes 322 + if (loopCount > 2) { 323 + $marqueeWrapper.css('transform', 'translateY(' + (o.direction === 'up' ? 0 : '-' + elHeight + 'px') + ')'); 324 + } 325 + 326 + animationCss = { 327 + 'transform': 'translateY(' + (o.direction === 'up' ? '-' + elHeight + 'px' : 0) + ')' 328 + }; 329 + } else if (o.startVisible) { 330 + // This loop moves the marquee out of the container 331 + if (loopCount === 2) { 332 + // Adjust the css3 animation as well 333 + if (animationCss3Str) { 334 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's linear'; 335 + } 336 + animationCss = { 337 + 'transform': 'translateY(' + (o.direction === 'up' ? '-' + elHeight + 'px' : containerHeight + 'px') + ')' 338 + }; 339 + loopCount++; 340 + } else if (loopCount === 3) { 341 + // Set the duration for the animation that will run forever 342 + o.duration = o._completeDuration; 343 + // Adjust the css3 animation as well 344 + if (animationCss3Str) { 345 + animationName = animationName + '0'; 346 + keyframeString = $.trim(keyframeString) + '0 '; 347 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite linear'; 348 + } 349 + _rePositionVertically(); 350 + } 351 + } else { 352 + _rePositionVertically(); 353 + animationCss = { 354 + 'transform': 'translateY(' + (o.direction === 'up' ? '-' + ($marqueeWrapper.height()) + 'px' : containerHeight + 'px') + ')' 355 + }; 356 + } 357 + } else { 358 + if (o.duplicated) { 359 + 360 + // Adjust the starting point of animation only when first loops finishes 361 + if (loopCount > 2) { 362 + $marqueeWrapper.css('transform', 'translateX(' + (o.direction === 'left' ? 0 : '-' + elWidth + 'px') + ')'); 363 + } 364 + 365 + animationCss = { 366 + 'transform': 'translateX(' + (o.direction === 'left' ? '-' + elWidth + 'px' : 0) + ')' 367 + }; 368 + 369 + } else if (o.startVisible) { 370 + // This loop moves the marquee out of the container 371 + if (loopCount === 2) { 372 + // Adjust the css3 animation as well 373 + if (animationCss3Str) { 374 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's linear'; 375 + } 376 + animationCss = { 377 + 'transform': 'translateX(' + (o.direction === 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')' 378 + }; 379 + loopCount++; 380 + } else if (loopCount === 3) { 381 + // Set the duration for the animation that will run forever 382 + o.duration = o._completeDuration; 383 + // Adjust the css3 animation as well 384 + if (animationCss3Str) { 385 + animationName = animationName + '0'; 386 + keyframeString = $.trim(keyframeString) + '0 '; 387 + animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite linear'; 388 + } 389 + _rePositionHorizontally(); 390 + } 391 + } else { 392 + _rePositionHorizontally(); 393 + animationCss = { 394 + 'transform': 'translateX(' + (o.direction === 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')' 395 + }; 396 + } 397 + } 398 + 399 + // fire event 400 + $this.trigger('beforeStarting'); 401 + 402 + // If css3 support is available than do it with css3, otherwise use jQuery as fallback 403 + if (css3AnimationIsSupported) { 404 + // Add css3 animation to the element 405 + $marqueeWrapper.css(animationString, animationCss3Str); 406 + var keyframeCss = keyframeString + ' { 100% ' + _objToString(animationCss) + '}', 407 + $styles = $marqueeWrapper.find('style'); 408 + 409 + // Now add the keyframe animation to the marquee element 410 + if ($styles.length !== 0) { 411 + // Bug fixed for jQuery 1.3.x - Instead of using .last(), use following 412 + $styles.filter(":last").html(keyframeCss); 413 + } else { 414 + $('head').append('<style>' + keyframeCss + '</style>'); 415 + } 416 + 417 + // Animation iteration event 418 + _prefixedEvent($marqueeWrapper[0], "AnimationIteration", function() { 419 + $this.trigger('finished'); 420 + }); 421 + // Animation stopped 422 + _prefixedEvent($marqueeWrapper[0], "AnimationEnd", function() { 423 + animate(); 424 + $this.trigger('finished'); 425 + }); 426 + 427 + } else { 428 + // Start animating 429 + $marqueeWrapper.animate(animationCss, o.duration, o.easing, function() { 430 + // fire event 431 + $this.trigger('finished'); 432 + // animate again 433 + if (o.pauseOnCycle) { 434 + _startAnimationWithDelay(); 435 + } else { 436 + animate(); 437 + } 438 + }); 439 + } 440 + // save the status 441 + $this.data('runningStatus', 'resumed'); 442 + }; 443 + 444 + // bind pause and resume events 445 + $this.on('pause', methods.pause); 446 + $this.on('resume', methods.resume); 447 + 448 + // If css3 animation is supported than call animate method at once 449 + if (css3AnimationIsSupported && o.allowCss3Support) { 450 + animate(); 451 + } else { 452 + // Starts the recursive method 453 + _startAnimationWithDelay(); 454 + } 455 + 456 + }); 457 + }; // End of Plugin 458 + // Public: plugin defaults options 459 + $.fn.marquee.defaults = { 460 + // If you wish to always animate using jQuery 461 + allowCss3Support: true, 462 + // works when allowCss3Support is set to true - for full list see http://www.w3.org/TR/2013/WD-css3-transitions-20131119/#transition-timing-function 463 + css3easing: 'linear', 464 + // requires jQuery easing plugin. Default is 'linear' 465 + // easing: 'linear', 466 + // pause time before the next animation turn in milliseconds 467 + delayBeforeStart: 1000, 468 + // 'left', 'right', 'up' or 'down' 469 + direction: 'left', 470 + // true or false - should the marquee be duplicated to show an effect of continues flow 471 + duplicated: false, 472 + // number of duplicates to create, default is 1 473 + duplicateCount: 1, 474 + // duration in milliseconds of the marquee in milliseconds 475 + duration: 5000, 476 + // Speed allows you to set a relatively constant marquee speed regardless of the width of the containing element. Speed is measured in pixels per second. 477 + speed: 0, 478 + // gap in pixels between the tickers 479 + gap: 20, 480 + // on cycle pause the marquee 481 + pauseOnCycle: false, 482 + // on hover pause the marquee - using jQuery plugin https://github.com/tobia/Pause 483 + // pauseOnHover: false, 484 + // the marquee is visible initially positioned next to the border towards it will be moving 485 + startVisible: false 486 + }; 487 + });
+12
public/style.css
··· 251 251 .table { 252 252 display: table; 253 253 } 254 + .w-full { 255 + width: 100%; 256 + } 254 257 .cursor-default\! { 255 258 cursor: default !important; 256 259 } ··· 266 269 .gap-3 { 267 270 gap: calc(var(--spacing) * 3); 268 271 } 272 + .gap-\[25px\] { 273 + gap: 25px; 274 + } 275 + .overflow-hidden { 276 + overflow: hidden; 277 + } 269 278 .border-black { 270 279 border-color: var(--color-black); 271 280 } ··· 276 285 .to-red-500\! { 277 286 --tw-gradient-to: var(--color-red-500) !important; 278 287 --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)) !important; 288 + } 289 + .mask-\[linear-gradient\(to_right\,transparent\,black_5\%\,black_95\%\,transparent\)\] { 290 + mask-image: linear-gradient(to right,transparent,black 5%,black 95%,transparent); 279 291 } 280 292 .p-4 { 281 293 padding: calc(var(--spacing) * 4);