Compare commits

..

No commits in common. 'main' and 'legacy_v2' have entirely different histories.

2
.gitignore vendored

@ -7,5 +7,3 @@ nginx/conf.d/default.conf
nextcloud/ nextcloud/
gitea/ gitea/
.env .env
portfolio-nuxt/.nuxt/
portfolio-nuxt/node_modules/

@ -60,13 +60,11 @@ services:
- ./portfolio/src:/src - ./portfolio/src:/src
environment: environment:
- VIRTUAL_HOST=${DOMAIN},www.${DOMAIN} - VIRTUAL_HOST=${DOMAIN},www.${DOMAIN}
#- VIRTUAL_PATH=/ - VIRTUAL_PATH=/
# For subdirectory baseURL needs to be set in app.js for static files and routes #- VIRTUAL_DEST=/
- VIRTUAL_PATH=/legacy
- VIRTUAL_DEST=/legacy
- VIRTUAL_PORT=3000 - VIRTUAL_PORT=3000
#- LETSENCRYPT_HOST=${DOMAIN},www.${DOMAIN},gitea.${DOMAIN} #this last one is for legacy support - LETSENCRYPT_HOST=${DOMAIN},www.${DOMAIN},gitea.${DOMAIN} #this last one is for legacy support
#- LETSENCRYPT_EMAIL=${EMAIL} - LETSENCRYPT_EMAIL=${EMAIL}
ports: ports:
- "3000:3000" - "3000:3000"
restart: always restart: always
@ -78,39 +76,6 @@ services:
#labels: #labels:
# com.github.nginx-proxy.nginx-proxy.keepalive: "64" # com.github.nginx-proxy.nginx-proxy.keepalive: "64"
portfolio-nuxt:
# NOTE: This is the rewrite of the frontend
# NOTE: The build process for nuxt seems to require that sharp be reinstalled in the .output folder
container_name: portfolio-nuxt
build: ./portfolio-nuxt
# To rebuild the site and the server run this
command: bash -c "npm run build && node .output/server/index.mjs"
# To just start the server run this
#command: bash -c "node .output/server/index.mjs"
# To start the server in dev mode
#command: bash -c "npm run dev -o"
volumes:
- portfolio-nuxt:/src/node_modules
- ./portfolio-nuxt:/src
environment:
- VIRTUAL_HOST=${DOMAIN},www.${DOMAIN}
- VIRTUAL_PATH=/
#- VIRTUAL_DEST=/dev
# For subdirectory baseURL needs to be set in nuxt config
#- VIRTUAL_PATH=/dev
#- VIRTUAL_DEST=/dev
- VIRTUAL_PORT=5000
- LETSENCRYPT_HOST=${DOMAIN},www.${DOMAIN},gitea.${DOMAIN} #this last one is for legacy support
- LETSENCRYPT_EMAIL=${EMAIL}
ports:
- "5000:5000"
restart: always
depends_on:
- restheart
- nginx-proxy
#labels:
# com.github.nginx-proxy.nginx-proxy.keepalive: "64"
mongo: mongo:
container_name: mongo container_name: mongo
# using mongo4 or mongo5 as opposed to mongo:6 for server status in mongo-express and because of bugs # using mongo4 or mongo5 as opposed to mongo:6 for server status in mongo-express and because of bugs
@ -384,4 +349,3 @@ volumes:
#nextcloud: #nextcloud:
acme: acme:
portfolio: portfolio:
portfolio-nuxt:

@ -9,14 +9,6 @@ location ^~ /.well-known/acme-challenge/ {
} }
## End of configuration add by letsencrypt container ## End of configuration add by letsencrypt container
# This is to set the content type to prevent downloading until I actually implement a proper pdf viewer
location ~ ^/api/(scores|pubs)(.*)/binary$ {
proxy_pass http://unboundedpress.org-357322ae39f93f572e23cd9edd3307e2ac5a321f;
# types { } default_type application/pdf;
proxy_hide_header Content-Type;
add_header Content-Type "application/pdf";
}
# The following are all for collabora routing # The following are all for collabora routing

@ -1,11 +0,0 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist
.DS_Store
.fleet
.idea

@ -1,2 +0,0 @@
shamefully-hoist=true
strict-peer-dependencies=false

@ -1,17 +0,0 @@
FROM node:18-alpine
WORKDIR /src
COPY package*.json ./
COPY . .
RUN apk add bash
RUN npm install
ENV NITRO_HOST=0.0.0.0
ENV NITRO_PORT=5000
EXPOSE 5000
# ENTRYPOINT ["npm", "run", "build", "node", ".output/server/index.mjs"]

@ -1,25 +0,0 @@
<template>
<div class="bg-white min-w-[800px] min-h-[80vh]">
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div>
</template>
<script setup>
useHead({
titleTemplate: 'Michael Winter'
})
</script>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.2s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

@ -1,313 +0,0 @@
<script>
export default {
name: 'CollapseTransition',
props: {
name: {
type: String,
required: false,
default: 'collapse',
},
dimension: {
type: String,
required: false,
default: 'height',
validator: (value) => {
return ['height', 'width'].includes(value)
},
},
duration: {
type: Number,
required: false,
default: 300,
},
easing: {
type: String,
required: false,
default: 'ease-in-out',
},
},
emits: ['before-appear', 'appear', 'after-appear', 'appear-cancelled', 'before-enter', 'enter', 'after-enter', 'enter-cancelled', 'before-leave', 'leave', 'after-leave', 'leave-cancelled'],
data() {
return {
cachedStyles: null,
}
},
computed: {
transition() {
const transitions = []
Object.keys(this.cachedStyles).forEach((key) => {
transitions.push(
`${this.convertToCssProperty(key)} ${this.duration}ms ${this.easing}`,
)
})
return transitions.join(', ')
},
},
watch: {
dimension() {
this.clearCachedDimensions()
},
},
methods: {
beforeAppear(el) {
// Emit the event to the parent
this.$emit('before-appear', el)
},
appear(el) {
// Emit the event to the parent
this.$emit('appear', el)
},
afterAppear(el) {
// Emit the event to the parent
this.$emit('after-appear', el)
},
appearCancelled(el) {
// Emit the event to the parent
this.$emit('appear-cancelled', el)
},
beforeEnter(el) {
// Emit the event to the parent
this.$emit('before-enter', el)
},
enter(el, done) {
// Because width and height may be 'auto',
// first detect and cache the dimensions
this.detectAndCacheDimensions(el)
// The order of applying styles is important:
// - 1. Set styles for state before transition
// - 2. Force repaint
// - 3. Add transition style
// - 4. Set styles for state after transition
// If the order is not right and you open any 2nd level submenu
// for the first time, the transition will not work.
this.setClosedDimensions(el)
this.hideOverflow(el)
this.forceRepaint(el)
this.setTransition(el)
this.setOpenedDimensions(el)
// Emit the event to the parent
this.$emit('enter', el, done)
// Call done() when the transition ends
// to trigger the @after-enter event.
setTimeout(done, this.duration)
},
afterEnter(el) {
// Clean up inline styles
this.unsetOverflow(el)
this.unsetTransition(el)
this.unsetDimensions(el)
this.clearCachedDimensions()
// Emit the event to the parent
this.$emit('after-enter', el)
},
enterCancelled(el) {
// Emit the event to the parent
this.$emit('enter-cancelled', el)
},
beforeLeave(el) {
// Emit the event to the parent
this.$emit('before-leave', el)
},
leave(el, done) {
// For some reason, @leave triggered when starting
// from open state on page load. So for safety,
// check if the dimensions have been cached.
this.detectAndCacheDimensions(el)
// The order of applying styles is less important
// than in the enter phase, as long as we repaint
// before setting the closed dimensions.
// But it is probably best to use the same
// order as the enter phase.
this.setOpenedDimensions(el)
this.hideOverflow(el)
this.forceRepaint(el)
this.setTransition(el)
this.setClosedDimensions(el)
// Emit the event to the parent
this.$emit('leave', el, done)
// Call done() when the transition ends
// to trigger the @after-leave event.
// This will also cause v-show
// to reapply 'display: none'.
setTimeout(done, this.duration)
},
afterLeave(el) {
// Clean up inline styles
this.unsetOverflow(el)
this.unsetTransition(el)
this.unsetDimensions(el)
this.clearCachedDimensions()
// Emit the event to the parent
this.$emit('after-leave', el)
},
leaveCancelled(el) {
// Emit the event to the parent
this.$emit('leave-cancelled', el)
},
detectAndCacheDimensions(el) {
// Cache actual dimensions
// only once to void invalid values when
// triggering during a transition
if (this.cachedStyles)
return
const visibility = el.style.visibility
const display = el.style.display
// Trick to get the width and
// height of a hidden element
el.style.visibility = 'hidden'
el.style.display = ''
this.cachedStyles = this.detectRelevantDimensions(el)
// Restore any original styling
el.style.visibility = visibility
el.style.display = display
},
clearCachedDimensions() {
this.cachedStyles = null
},
detectRelevantDimensions(el) {
// These properties will be transitioned
if (this.dimension === 'height') {
return {
height: `${el.offsetHeight}px`,
paddingTop:
el.style.paddingTop || this.getCssValue(el, 'padding-top'),
paddingBottom:
el.style.paddingBottom || this.getCssValue(el, 'padding-bottom'),
}
}
if (this.dimension === 'width') {
return {
width: `${el.offsetWidth}px`,
paddingLeft:
el.style.paddingLeft || this.getCssValue(el, 'padding-left'),
paddingRight:
el.style.paddingRight || this.getCssValue(el, 'padding-right'),
}
}
return {}
},
setTransition(el) {
el.style.transition = this.transition
},
unsetTransition(el) {
el.style.transition = ''
},
hideOverflow(el) {
el.style.overflow = 'hidden'
},
unsetOverflow(el) {
el.style.overflow = ''
},
setClosedDimensions(el) {
Object.keys(this.cachedStyles).forEach((key) => {
el.style[key] = '0'
})
},
setOpenedDimensions(el) {
Object.keys(this.cachedStyles).forEach((key) => {
el.style[key] = this.cachedStyles[key]
})
},
unsetDimensions(el) {
Object.keys(this.cachedStyles).forEach((key) => {
el.style[key] = ''
})
},
forceRepaint(el) {
// Force repaint to make sure the animation is triggered correctly.
// Thanks: https://markus.oberlehner.net/blog/transition-to-height-auto-with-vue/
// eslint-disable-next-line no-unused-expressions
getComputedStyle(el)[this.dimension]
},
getCssValue(el, style) {
return getComputedStyle(el, null).getPropertyValue(style)
},
convertToCssProperty(style) {
// Example: convert 'paddingTop' to 'padding-top'
// Thanks: https://gist.github.com/tan-yuki/3450323
const upperChars = style.match(/([A-Z])/g)
if (!upperChars)
return style
for (let i = 0, n = upperChars.length; i < n; i++) {
style = style.replace(
new RegExp(upperChars[i]),
`-${upperChars[i].toLowerCase()}`,
)
}
if (style.slice(0, 1) === '-')
style = style.slice(1)
return style
},
},
}
</script>
<template>
<transition
:name="name"
@before-appear="beforeAppear"
@appear="appear"
@after-appear="afterAppear"
@appear-cancelled="appearCancelled"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<slot />
</transition>
</template>

@ -1,25 +0,0 @@
import type { Story } from '@storybook/vue3'
import Collapsible from './Collapsible.vue'
export default {
title: 'Components/Collapsible',
component: Collapsible,
args: {
modelValue: false,
title: 'Item',
content: 'lorem ipsum dolor sit amet',
},
}
const Template: Story = (args, { argTypes }) => ({
components: { Collapsible },
setup() {
return { args, argTypes }
},
template: `
<Collapsible v-bind="args"/>
`,
})
export const Default = Template.bind({})
Default.args = {}

@ -1,91 +0,0 @@
<script lang="ts" setup>
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
import { ref, toRefs, watch } from 'vue'
import CollapseTransition from './CollapseTransition.vue'
import Modal from '../Modal/Modal.vue';
const props = withDefaults(
defineProps<{
modelValue?: boolean
title: string
content?: string
classes?: {
wrapper?: string
button?: string
title?: string
panel?: string
}
}>(),
{
modelValue: false,
},
)
const emit = defineEmits([
'update:modelValue',
'change',
'toggle',
'open',
'close',
])
const { modelValue } = toRefs(props)
const isOpen = ref(modelValue.value)
watch(modelValue, (val) => {
isOpen.value = val
})
watch(isOpen, (val) => {
emit('update:modelValue', val)
emit('change', val)
if (val)
emit('open')
else
emit('close')
})
const toggle = () => {
emit('toggle')
isOpen.value = !isOpen.value
}
</script>
<template>
<Disclosure v-slot="{ open }" as="div">
<DisclosureButton
class="
flex
items-center
justify-between
w-full
text-left
rounded-lg
focus:outline-none
focus-visible:ring
focus-visible:ring-blue-50
focus-visible:ring-opacity-75
"
:class="classes?.button"
type="button"
@click="toggle"
>
<div class="inline-flex w-full">
<Icon
name="heroicons:chevron-down"
:class="isOpen ? 'transform rotate-180' : ''"
class="w-5 h-5"
/>
<slot name="title"></slot>
</div>
</DisclosureButton>
<CollapseTransition>
<div v-show="isOpen">
<DisclosurePanel static class="pb-2 text-15" :class="classes?.panel">
<slot name="content"></slot>
</DisclosurePanel>
</div>
</CollapseTransition>
</Disclosure>
</template>

@ -1,38 +0,0 @@
import type { Story } from '@storybook/vue3'
import CollapsibleGroup from './CollapsibleGroup.vue'
const genItems = (length = 5): any[] =>
Array.from({ length }, (_, v) => ({
title: `Item ${v + 1}`,
content: `lorem ipsum ${v + 1}`,
}))
const items = genItems(5)
export default {
title: 'Components/CollapsibleGroup',
component: CollapsibleGroup,
args: {
modelValue: false,
accordion: false,
items,
},
}
const Template: Story = (args, { argTypes }) => ({
components: { CollapsibleGroup },
setup() {
return { args, argTypes }
},
template: `
<CollapsibleGroup v-bind="args"/>
`,
})
export const Default = Template.bind({})
Default.args = {}
export const Accordion = Template.bind({})
Accordion.args = {
accordion: true,
}

@ -1,55 +0,0 @@
<script lang="ts" setup>
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
import { ref, toRefs, watch } from 'vue'
import Icon from '../Icon/index.vue'
import Collapsible from './Collapsible.vue'
interface CollapsibleItem {
title: string
content: string
isOpen?: boolean
}
const props
= defineProps<{
items?: CollapsibleItem[]
classes?: {
wrapper?: string
button?: string
title?: string
panel?: string
}
accordion?: boolean
}>()
const { items } = toRefs(props)
const children = ref(props.items)
watch(items, (val) => {
children.value = val
})
const onToggle = (item: CollapsibleItem) => {
if (props.accordion) {
children.value.forEach((child) => {
child.isOpen = false
})
item.isOpen = true
}
}
</script>
<template>
<div class="w-full p-2" :class="classes?.wrapper">
<slot>
<Collapsible
v-for="(item, idx) in children"
:key="idx"
v-bind="item"
v-model="item.isOpen"
@toggle="onToggle(item)"
/>
</slot>
</div>
</template>

@ -1,61 +0,0 @@
<template>
<Swiper
:autoHeight="true"
:loop="true"
:spaceBetween="30"
:centeredSlides="true"
:autoplay="{
delay: 6000,
disableOnInteraction: false,
pauseOnMouseEnter: true
}"
:effect="'fade'"
:fadeEffect="{
crossFade: true
}"
:pagination="{
clickable: true,
}"
:style="{
'--swiper-navigation-color': 'rgb(71 85 105)',
'--swiper-pagination-color': 'rgb(71 85 105)',
'--swiper-pagination-bottom': '0%'
}"
:modules="[SwiperAutoplay, SwiperPagination, SwiperNavigation, SwiperEffectFade]"
>
<SwiperSlide v-for="(item, index) in upcoming_events" class="bg-zinc-100 h-full">
<div class="gap-1 w-[100%] mt-1 mb-1 text-sm h-full">
<div>
{{ item.formatted_date }}: {{item.venue.city}}, {{item.venue.state}}
<div class="text-[#7F7F7F]">
{{ item.venue.name }}
</div>
</div>
</div>
<!-- Comment
<div v-for="performance in item.program">
<div class="italic text-sm ml-16 pt-1">{{performance.work}}</div>
<div v-if="performance.ensemble" class="ml-20">
{{ performance.ensemble }}
</div>
<div v-for="performer in performance.performers" class="ml-20">
{{ performer.name }} -
<span v-for="(instrument, index) in performer.instrument_tags">
<span v-if="index !== 0">, </span>
{{ instrument }}
</span>
</div>
</div>
-->
</SwiperSlide>
</Swiper>
</template>
<script>
export default {
props: ['upcoming_events']
}
</script>

@ -1,54 +0,0 @@
<template>
<div class="inline-flex p-1 min-w-[25px]">
<div v-show="visible" class="bg-black rounded-full text-xs inline-flex" >
<NuxtLink @click.native="audioPlayerStore.setSoundCloudTrackID(work.soundcloud_trackid)" v-if="type === 'score'" class="inline-flex p-1" :to="link">
<Icon name="ion:book-sharp" color="white" />
</NuxtLink>
<NuxtLink v-else-if="type === 'document'" class="inline-flex p-1" :to="link">
<Icon name="ion:book-sharp" color="white" />
</NuxtLink>
<NuxtLink v-else-if="type === 'buy'" class="inline-flex p-1" :to="link">
<Icon name="bxs:purchase-tag" color="white" />
</NuxtLink>
<NuxtLink v-else-if="type === 'email'" class="inline-flex p-1" :to="link">
<Icon name="ic:baseline-email" color="white" />
</NuxtLink>
<NuxtLink v-else-if="type === 'discogs'" class="inline-flex p-1" :to="link">
<Icon name="simple-icons:discogs" color="white" />
</NuxtLink>
<button @click="audioPlayerStore.setSoundCloudTrackID(work.soundcloud_trackid)" v-else-if="type === 'audio'" class="inline-flex p-1">
<Icon name="wpf:speaker" color="white" />
</button>
<button @click="modalStore.setModalProps('video', 'aspect-video', true, '', '', work.vimeo_trackid)" v-else-if="type === 'video'" class="inline-flex p-1">
<Icon name="fluent:video-48-filled" color="white" />
</button>
<button @click="modalStore.setModalProps('image', 'aspect-auto', true, 'images', work.gallery, '')" v-else="type === 'image'" class="inline-flex p-1">
<Icon name="mdi:camera" color="white" />
</button>
</div>
</div>
</template>
<script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore"
import { useModalStore } from "@/stores/ModalStore"
const audioPlayerStore = useAudioPlayerStore()
const modalStore = useModalStore()
</script>
<script>
export default {
props: ['type', 'work', 'visible', 'link']
}
</script>

@ -1,37 +0,0 @@
<template>
<Swiper
:autoHeight="true"
:loop="true"
:spaceBetween="30"
:centeredSlides="true"
:autoplay="{
delay: 4000,
disableOnInteraction: false,
pauseOnMouseEnter: true
}"
:pagination="{
clickable: true,
}"
:navigation="true"
:style="{
'--swiper-navigation-color': 'rgb(71 85 105)',
'--swiper-pagination-color': 'rgb(71 85 105)',
'--swiper-pagination-bottom': 'auto',
'--swiper-pagination-top': '1rem',
'--swiper-navigation-top-offset': '5rem'
}"
:modules="[SwiperAutoplay, SwiperPagination, SwiperNavigation]"
>
<SwiperSlide v-for="image in gallery" class="p-10 bg-zinc-100">
<nuxt-img :src="'https://unboundedpress.org/api/' + bucket + '.files/' + image.image_id + '/binary'"
quality="50"/>
</SwiperSlide>
</Swiper>
</template>
<script>
export default {
props: ['gallery', 'bucket']
}
</script>

@ -1,3 +0,0 @@
<template>
<div class="fixed inset-0 bg-black/50 z-15 transition duration-300" />
</template>

@ -1,110 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
import {
Dialog,
DialogPanel,
TransitionChild,
TransitionRoot,
} from '@headlessui/vue'
const props = withDefaults(
defineProps<{
modelValue?: boolean
persistent?: boolean
fullscreen?: boolean
}>(),
{
modelValue: false,
persistent: false,
fullscreen: false,
},
)
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
}>()
const { modelValue } = toRefs(props)
const isOpen = ref(modelValue.value)
watch(modelValue, (value) => {
isOpen.value = value
})
function closeModal() {
isOpen.value = false
}
function openModal() {
isOpen.value = true
}
function onModalClose() {
if (!props.persistent)
closeModal()
}
watch(isOpen, (value) => {
emit('update:modelValue', value)
})
const api = {
isOpen,
open: openModal,
close: closeModal,
}
provide('modal', api)
</script>
<template>
<slot name="activator" :open="openModal" :on="{ click: openModal }" />
<TransitionRoot appear :show="isOpen" as="template">
<Dialog as="div" class="relative z-20" @close="onModalClose">
<TransitionChild
as="template"
enter="duration-300 ease-out"
enter-from="opacity-0"
enter-to="opacity-100"
leave="duration-200 ease-in"
leave-from="opacity-100"
leave-to="opacity-0"
>
<div class="fixed inset-0 bg-black bg-opacity-25" />
</TransitionChild>
<div class="fixed inset-0 overflow-y-auto">
<div
class="flex min-h-full items-center justify-center text-center"
:class="{
'p-4': !fullscreen,
}"
>
<TransitionChild
as="template"
enter="duration-300 ease-out"
enter-from="opacity-0 scale-95"
enter-to="opacity-100 scale-100"
leave="duration-200 ease-in"
leave-from="opacity-100 scale-100"
leave-to="opacity-0 scale-95"
>
<DialogPanel
class="w-full transform overflow-hidden bg-white text-left align-middle shadow-xl transition-all"
:class="{
'h-screen': fullscreen,
'max-w-[85vw] rounded-lg': !fullscreen,
}"
>
<slot />
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>

@ -1,9 +0,0 @@
<script setup lang="ts">
import { DialogDescription } from '@headlessui/vue'
</script>
<template>
<DialogDescription class="px-4 py-3 text-sm text-gray-800">
<slot />
</DialogDescription>
</template>

@ -1,9 +0,0 @@
<script setup lang="ts">
// import { ref } from 'vue'
</script>
<template>
<div class="px-4 py-3">
<slot />
</div>
</template>

@ -1,34 +0,0 @@
<script setup lang="ts">
import { DialogTitle } from '@headlessui/vue'
interface Props {
dismissable?: boolean
titleClass?: string
}
defineProps<Props>()
const api = inject('modal')
</script>
<template>
<DialogTitle
as="div"
class="flex gap-2 justify-between items-center px-4 pt-3"
>
<h3
class="text-lg font-medium leading-6 text-gray-900"
:class="titleClass"
>
<slot />
</h3>
<slot v-if="dismissable" name="dismissable">
<button
class="text-2xl text-gray-500 appearance-none px-2 -mr-2"
@click="api.close"
>
&times;
</button>
</slot>
</DialogTitle>
</template>

@ -1,79 +0,0 @@
<template>
<div class="grid grid-cols-[63%,35%] w-full font-thin sticky top-0 bg-white p-2 z-20">
<div>
<div class="text-5xl p-2"> <NuxtLink to='/'>michael winter</NuxtLink></div>
<div class="inline-flex text-2xl ml-4">
<NuxtLink class="px-3" to='/'>works</NuxtLink>
<NuxtLink class="px-3" to='/events'>events</NuxtLink>
<NuxtLink class="px-3" to='/about'>about</NuxtLink>
<NuxtLink class="px-3" to='https://unboundedpress.org/code'>code</NuxtLink>
<NuxtLink class="px-3 block" to='https://unboundedpress.org/legacy'>legacy</NuxtLink>
</div>
<!-- hdp link while active -->
<!------
<div class="inline-flex text-2xl ml-4 font-bold">
<NuxtLink class="px-3" to='/a_history_of_the_domino_problem'>A HISTORY OF THE DOMINO PROBLEM | 17.11 - 01.12.2023 </NuxtLink>
</div>
--->
</div>
<!-- TODO: this needs to be automatically flipped off when there are no upcoming events-->
<!------
<div class="px-1 bg-zinc-100 rounded-lg text-center">
<div class="text-sm">upcoming events</div>
<EventSlider :upcoming_events="upcoming_events" class="max-w-[95%] min-h-[80%]"></EventSlider>
</div>
-->
</div>
<slot /> <!-- required here only -->
<div class="fixed bottom-0 bg-white p-2 w-full flex justify-center z-20">
<iframe width="400rem" height="20px" scrolling="no" frameborder="no" allow="autoplay" v-if="audioPlayerStore.soundcloud_trackid !== 'undefined'"
:src="'https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' + audioPlayerStore.soundcloud_trackid + '&inverse=false&auto_play=true&show_user=false'"></iframe>
</div>
<Modal v-model="modalStore.isOpen">
<ModalBody :class="modalStore.aspect">
<ImageSlider v-if="modalStore.type === 'image'" :bucket="modalStore.bucket" :gallery="modalStore.gallery"></ImageSlider>
<iframe v-if="modalStore.type === 'video'" :src="'https://player.vimeo.com/video/' + modalStore.vimeo_trackid" width="100%" height="100%" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</ModalBody>
</Modal>
</template>
<script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore"
import { useModalStore } from "@/stores/ModalStore"
const audioPlayerStore = useAudioPlayerStore()
const modalStore = useModalStore()
const route = useRoute()
if(route.params.files == 'scores') {
const { data: work } = await useFetch('https://unboundedpress.org/api/works?filter={"score":"' + route.params.filename + '"}', {
transform: (work) => {
if(work[0].soundcloud_trackid){
audioPlayerStore.setSoundCloudTrackID(work[0].soundcloud_trackid)
} else {
audioPlayerStore.clearSoundCloudTrackID()
}
return work[0]
}
})
}
//const today = Date.now(); //annoying this does not work
const today = 1715247305793;
const { data: upcoming_events } = await useFetch("https://unboundedpress.org/api/events?filter={'start_date':{'$gte':{'$date':" + today + "}}}", {
transform: (upcoming_events) => {
for (const event of upcoming_events) {
let date = new Date(event.start_date.$date)
event.formatted_date = ("0" + (date.getMonth() + 1)).slice(-2) + "." + ("0" + date.getDate()).slice(-2) + "." + date.getFullYear()
}
return upcoming_events.sort((a,b) => a.start_date.$date - b.start_date.$date)
}
})
</script>

@ -1,35 +0,0 @@
//import { defineNuxtConfig } from 'nuxt3'
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss', '@nuxt/image', 'nuxt-icon', '@pinia/nuxt', 'nuxt-headlessui', 'nuxt-swiper'],
extends: ['nuxt-umami'],
image: {
domains: ['unboundedpress.org']
},
app: {
//baseURL: "/dev/",
pageTransition: { name: 'page', mode: 'out-in' },
head: {
viewport: 'width=device-width'
},
},
appConfig: {
umami: {
id: '51f4f246-9c2e-4a86-9ffb-7a7967d9013d',
host: 'https://analytics.umami.is/',
version: 2
},
},
routeRules: {
'/cv': { redirect: '/legacy/cv' },
'/works_list': { redirect: '/legacy/works_list' },
'/hdp': { redirect: '/a_history_of_the_domino_problem' },
},
nitro: {
prerender: { crawlLinks: true}
},
experimental: {
payloadExtraction: true
}
})

File diff suppressed because it is too large Load Diff

@ -1,26 +0,0 @@
{
"name": "nuxt-app",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxt/image": "^1.0.0-rc.1",
"@nuxtjs/tailwindcss": "^6.7.0",
"@types/node": "^18",
"nuxt-headlessui": "^1.1.4",
"nuxt-icon": "^0.4.1"
},
"dependencies": {
"nuxt": "^3.6.0",
"@pinia/nuxt": "^0.4.11",
"nuxt-swiper": "^1.1.0",
"nuxt-umami": "^2.4.2",
"pinia": "^2.1.3",
"sharp": "^0.32.1"
}
}

@ -1,27 +0,0 @@
<template>
<div class="flex min-h-full items-center justify-center text-center">
<embed v-if="route.params.filename.split('.').pop()==='pdf'" :src="'https://unboundedpress.org/api/' + route.params.files + '.files/' + file_metadata._id.$oid + '/binary'" class="w-[85%] h-[88vh]"/>
<nuxt-img v-else-if="route.params.filename.split('.').pop()==='jpg'" :src="'https://unboundedpress.org/api/' + route.params.files + '.files/' + file_metadata._id.$oid + '/binary'" class="w-[85%]"/>
</div>
</template>
<script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore"
const audioPlayerStore = useAudioPlayerStore()
const route = useRoute()
const { data: file_metadata } = await useFetch('https://unboundedpress.org/api/' + route.params.files + '.files?filter={"filename":"' + route.params.filename + '"}', {
//lazy: true,
//server: false,
transform: (file_metadata) => {
return file_metadata[0]
}
})
useHead({
titleTemplate: 'Michael Winter - Files - ' + route.params.filename
})
</script>

@ -1,346 +0,0 @@
<template>
<div class="bg-contain bg-no-repeat bg-center rounded-lg m-5 gap-10 bg-[#0A0A19] py-4 text-2xl text-white py-4 mb-10 overflow-hidden" :style="{ backgroundImage: `url(${image})`}">
<div class="rounded-lg w-full sticky top-[10px] grid grid-cols-[63%,35%]">
<div>
<div class="p-5 text-5xl font-bold">a history of the domino problem</div>
<div>
<div class="inline-flex text-2xl ml-4 mb-5">
<a href="#about" class="px-3">about</a>
<a href="#exhibition" class="px-3">exhibition</a>
<a href="#events" class="px-3">events</a>
<a href="#participants" class="px-3">participants</a>
</div>
<div class="inline-flex text-2xl ml-4 mb-5">
<a href="#media" class="px-3">media</a>
<a href="#contributors" class="px-3">contributors</a>
<a href="#resources" class="px-3">resources</a>
<a href="#contact" class="px-3">contact/press</a>
</div>
</div>
</div>
<div>
For the Lecture-Concert on 22 Nov 2023, Registration recommended. Sign up <NuxtLink class="text-2xl font-bold" to='https://www.eventbrite.de/e/a-history-of-the-domino-problem-lecture-concert-tickets-707700981687'>HERE</NuxtLink>.
</div>
</div>
<!---
<Swiper
:loop="true"
:spaceBetween="30"
:centeredSlides="true"
:pagination='{
clickable: true,
renderBullet: function (index, className) {
return "<span class=" + className + ">" + ["about", "exhibition", "events", "participants"][index] + "</span>";
}
}'
:navigation="false"
:style="{
'--swiper-pagination-color': 'rgb(255 255 255)',
'--swiper-pagination-bullet-horizontal-gap': '80px',
'--swiper-pagination-bullet-inactive-opacity': '0.8',
'--swiper-pagination-bullet-size': '0px',
'--swiper-pagination-bottom': 'auto',
'--swiper-pagination-top': '1rem',
'--swiper-pagination-margin-left': '0px',
}"
:hashNavigation="{
watchState: true,
}"
:modules="[SwiperAutoplay, SwiperPagination, SwiperNavigation, SwiperHashNavigation]"
class="max-w-[95vw]"
>
--->
<Swiper
:loop="true"
:spaceBetween="30"
:centeredSlides="true"
:pagination="false"
:navigation="false"
:hashNavigation="{
watchState: true,
}"
:modules="[SwiperAutoplay, SwiperPagination, SwiperNavigation, SwiperHashNavigation]"
>
<SwiperSlide data-hash="about" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="overflow-auto">
<p class="mb-5">
<span class="italic">a history of the domino problem</span> is a performance-installation that traces the history of an epistemological problem in mathematics about how things that one could never imagine fitting together, actually come together and unify in unexpected ways. The work comprises a set of musical compositions and a kinetic sculpture that sonify and visualize rare tilings (more commonly known as mosaics) constructed from dominoes. The dominoes in these tilings are similar yet slightly different than those used in the popular game of the same name. As opposed to rectangles divided into two regions with numbers between 1 and 6, they are squares where each of the 4 edges is assigned a number (typically represented by a corresponding color or alternatively, pattern) called <NuxtLink to='https://en.wikipedia.org/wiki/Wang_tile'>Wang tiles</NuxtLink>. Like in the game, the rule is that edges of adjacent dominoes in a tiling must match.
</p>
<p class="mb-5">
The tilings sonified and visualized in <span class="italic">a history of the domino problem</span> are rare because there is no systematic way to find them. This is due to the fact that they are <NuxtLink to='https://en.wikipedia.org/wiki/Aperiodic_tiling'><span class="italic">aperiodic</span></NuxtLink>. One can think of an aperiodic tiling as an infinite puzzle with a peculiar characteristic: given unlimited copies of dominoes with a finite set of color/pattern combinations for the edges, on can form a tiling that expands infinitely. However, in that solution, any repeating structure in the tiling will eventually be interrupted. This phenomenon is one of the most intriguing aspects of the work. As the music and the visuals are derived from the tilings, the resulting textures are always shifting ever so slightly.
</p>
<p>
The original Domino Problem asked if there exists an algorithm/computer program that, when given as input a finite set of dominoes with varying color combinations for the edges, can output a binary answer, `yes' or `no', whether or not copies of that set can form an infinite tiling. The problem was first posed by Hao Wang in 1961, who conjectured that the answer is positive and that such an algorithm does exist. The existence of aperiodic tilings would mean that such an algorithm <span class="italic">does not</span> exist. However, in 1964, his student, Robert Berger, proved him wrong by discovering an infinite, aperiodic tiling constructed with copies of a set of 20,426 dominoes. The resolution of Wang's original question led to new questions and mathematicians took on the challenge of finding the smallest set of dominoes that would construct an infinite aperiodic tiling. Over the past 60 years, this number has been continually reduced with the contributions of many different mathematicians until the most recent discovery of a set of 11 dominoes along with a proof that no smaller sets exist. It is a remarkable narrative/history of a particular epistemological problem that challenged a group of people not only to solve it, but to understand it to the extent possible.
</p>
</div>
</span>
</SwiperSlide>
<SwiperSlide data-hash="exhibition" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="overflow-auto">
<div class="mb-5 text-3xl italic font-bold">
a few thoughts on how things fit together...
</div>
<div class="mb-5">
(free entrance)
</div>
<br>
<div class="mb-5">
in collaboration with MAREIKE YIN-YEE LEE
</div>
<div class="mb-5">
Exhibition Opening - 17 Nov 2023 | 19 Uhr
</div>
<div class="mb-5">
Exhibition Closing - 01 Dec 2023 | 19 Uhr
</div>
<div class="mb-5">
Gallery Hours - Wednesday to Friday | 13 - 19 Uhr & Saturday | 12 - 18 Uhr
</div>
<div class="mb-5">
Lichthof Ost, HU Berlin Hauptgebäude, Campus Mitte, Unter den Linden 6 (U-Bahn Unter den Linden oder Museuminsel)
</div>
<div class="mb-5">
<NuxtLink class="px-3" to='https://unboundedpress.org/pubs/a_few_thoughts_exhibition_poster.pdf'><nuxt-img class="w-[500px]" src="/hdp_images/hdp_exhibition_poster_digital.jpeg"/></NuxtLink>
</div>
<div class="mb-5">
<NuxtLink class="px-3" to='https://unboundedpress.org/hdp_images/lichthof_ost_map.jpeg'><nuxt-img class="w-[500px]" src="/hdp_images/lichthof_ost_map.jpeg"/></NuxtLink>
</div>
<div class="mb-5">
<span class="font-bold">About the Exhibition</span>
<br>
<br>
The exhibition will feature individual and collaborative works by Michael Winter and Mareike Yin-Yee Lee in a constellation designed specifically for the Lichthof Ost exhibition room of the Humboldt University. The original kinetic sculpture Winter created to visualize the aperiodic tilings of the history of the domino problem will be juxtaposed with recent works by Yin-Yee Lee as well as collaboratively created realizations of the tilings. The works on display by Yin-Yee Lee will highlight selections from her Hidden Lakes and Missing Pieces series in which enigmatic outlines of lakes and various shapes encourage observers to perceive similarities and differences in form, pattern, and repetition between the pieces and to mentally fill in blank space. The collaborative realizations of the tilings will include prints generated by Winter with the aid of a computer that incorporate images and color schemes by Yin-Yee Lee as well as a floor mosaic of drawings on mirrors. The exhibition plays on the macro versus the micro, transformation, and how topologies of various color combinations, relationships between shapes and gradients reflect in space in order to illuminate "a few thoughts on how things fit together..."
</div>
<br>
<div class="mb-5">
<span class="font-bold">About the Kinetic Sculpture</span>
<br>
<br>
The kinetic sculpture displays the mosaics using visual cryptography. In visual cryptography, a message is encrypted by dividing the information of the message into two `shadow' images, which look completely random and independent of each other. The message is decrypted and revealed when the shadow images are combined/overlayed in a precise orientation. In the kinetic sculpture of a history of the domino problem, the shadow images are printed on photomasks, which are essentially high-resolution transparencies: quartz wafers with a chrome coating etched at a pixel size ranging from nano- to micrometers. A high-precision, motorized multiaxis stage aligns the finely printed shadow images to reveal the mosaics (along with 3 other images of poetic texts inspired by the history of the Domino Problem).
</div>
</div>
</span>
</SwiperSlide>
<SwiperSlide data-hash="events" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="overflow-auto">
<div class="mb-5">
<span class="font-bold">Exhibition Opening - 17 Nov 2023 | 19 Uhr</span>
<br>
Lichthof Ost, HU Berlin Hauptgebäude, Campus Mitte, Unter den Linden 6 (U-Bahn Unter den Linden oder Museuminsel)
</div>
<div class="mb-5">
<span class="font-bold">Exhibition Closing - 01 Dec 2023 | 19 Uhr</span>
<br>
Lichthof Ost, HU Berlin Hauptgebäude, Campus Mitte, Unter den Linden 6 (U-Bahn Unter den Linden oder Museuminsel)
</div>
<div class="mb-5">
<span class="font-bold">Public lecture + Concert (free entrance) - 22 Nov 2023 | 19:30 Uhr (doors open at 19:00 Uhr)</span>
<br>
with Prof. JARKKO KARI (Turku University), moderated by Prof. Dr. GAËTAN BOROT (HU Berlin)
<br>
the abstract of Prof. JARKKO KARI's Lecture is provided below
<br>
performance by KALI ENSEMBLE
<br>
Fritz-Reuter-Saal, HU Berlin Universitätsgebäude (am Hegelplatz), Dorotheenstraße 24 (U-Bahn Unter den Linden oder Museuminsel)
</div>
<div class="mb-5">
<span class="font-bold">Concert - 23 Nov 2023 | 20:30 Uhr (doors open at 20:00 Uhr)</span>
<br>
performance by KALI ENSEMBLE
<br>
<NuxtLink to='https://www.km28.de/'>KM28</NuxtLink>
<br>
Karl-Marx-Str. 28, 12043 Berlin (U-Bahn Karl-Marx-Platz)
<br>
(entry by donation, with proceeds going to the Kali Ensemble)
</div>
<br>
<div class="mb-5">
<br>
About the Public lecture
<br>
<span class="font-bold">From Wang Tiles to the Domino Problem: A Tale of Aperiodicity</span>
<br>
<br>
This presentation delves into the remarkable history of aperiodic tilings and the domino problem. Aperiodic tile sets refer to collections of tiles that can only tile the plane in a non-repeating, or non-periodic, manner. Such sets were not believed to exist until 1964 when R. Berger introduced the first aperiodic set consisting of an astonishing 20,426 Wang tiles. Over the years, ongoing research led to significant advancements, culminating in 2015 with the discovery of a mere 11 Wang tiles by E. Jeandel and M. Rao, alongside a computer-assisted proof of their minimality. Simultaneously, researchers found even smaller aperiodic sets composed of polygon-shaped tiles. Notably, Penrose's kite and dart tiles emerged as early examples, and most recently, a groundbreaking discovery was made - a solitary aperiodic tile known as the "hat" that can tile the plane exclusively in a non-periodic manner. Aperiodic tile sets are intimately connected with the domino problem that asserts how certain tile sets can tile the plane without us ever being able to establish their tiling nature with absolute certainty. Moreover, aperiodic tilings hold a distinct visual aesthetic allure. In today's musical presentation, their artistic appeal transcends the visual domain and extends into the realm of music.
<br>
-Jarkko Kari
</div>
</div>
</span>
</SwiperSlide>
<SwiperSlide data-hash="participants" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="max-h-[800px] overflow-auto">
<div class="mb-5 py-10">
<NuxtLink class="text-3xl font-bold" to='https://unboundedpress.org/'>Michael Winter - composer | sound artist</NuxtLink>
<div class="grid grid-cols-[20%,70%] p-5">
<nuxt-img src="/hdp_images/michael.jpg"/>
<div class="px-5">
<p class="mb-5">
My practice as a composer and sound artist is diverse, ranging from music created by digital and acoustic instruments to installations and kinetic sculptures. Each piece typically explores one simple process and often reflects various related interests of mine such as phenomenology, mathematics, epistemology, algorithmic information theory, and the history of science. To me, everything we experience is computable. Given this digital philosophy, I acknowledge even my most open works as algorithmic; and, while not always apparent on the surface of any given piece, the considerations of computability and epistemology are integral to my practice. I often reconcile epistemological limits with artistic practicality by considering and addressing the limits of computation from an artistic and experiential vantage point and by collaborating with other artists, mathematicians, and scientists in order to integrate objects, ideas, and texts from various domains as structural elements in my pieces. My work also aims to subvert discriminatory conventions and hierarchies by exploring alternative forms of presentation and interaction, often with minimal resources and low information.
</p>
<p>
My work has been presented at venues and festivals throughout the world such as REDCAT, in Los Angeles; the Ostrava Festival of New Music in the Czech Republic; Tsonami Arte Sonoro Festival in Valparaiso, Chile; the Huddersfield New Music Festival in the United Kingdom; and Umbral Sesiones at the Museo de Arte Contemporáneo in Oaxaca, Mexico. Recordings of my music have been released by XI Records, Another Timbre, New World Records, Edition Wandelweiser, Bahn Mi Verlag, Tsonami Records, and Pogus Productions. In 2008, I co-founded the wulf., a Los Angeles-based organization dedicated to experimental performance and art. From 2018 to 2019, I was a fellow / artist-in-residence at the Akademie Schloss Solitude in Stuttgart, Germany. I currently reside in Berlin.
</p>
</div>
</div>
</div>
<div class="mb-5 py-10">
<NuxtLink class="text-3xl font-bold" to='http://www.mareikelee.com/'>MAREIKE YIN-YEE LEE - visual artist</NuxtLink>
<div class="grid grid-cols-[20%,70%] p-5">
<nuxt-img src="/hdp_images/mareike.jpg"/>
<div class="px-5">
Mareike YinYee Lees multidisciplinary practice encompasses drawing, video, sculpture, found and made objects, printmaking, and artist books. Current works include installations, recordings and live performances produced in collaboration with musicians and composers with an emphasis on the relation between sight and sound. How we approach, perceive and respond to these form the basis of her recent works manifestations. Her immersive, site-specific installations explore the complex and tenuous nature of communication and how we experience space, drawing on gesture, sound, and memory to elicit responses that cannot be put into words. She redefines the architecture and temporality of the spaces in which she works. Lees work plays with the spaces between, across, and beyond, embracing the undefinable and subtle gradations, forging a language of colour, tone and space that seeks to articulate microcosms of daily life and sustained contemplation. Lee studied at Universität der Künste, Berlin, Germany; University of Toronto, Toronto, Canada; and Nova Scotia College of Art and Design, Nova Scotia, Canada, where she was awarded the Joseph Beuys Scholarship and the Canada Millennium Award of Excellence. Recent projects include exhibitions and performances at Kunsthaus Kule Berlin (2020), Kunstmuseum Kloster Unser Lieben Frauen Magdeburg (2019), Galerie Kunstpunkt Berlin (2018), Kunstbezirk Stuttgart, Kunst(zeug)haus Rapperswil- Jona Switzerland (2017), Kunsthaus Interlaken (2017), Neuer Kunstverein, Aschaffenburg (2016), and KW Institute for Contemporary Art, Berlin (2016).
</div>
</div>
</div>
<div class="mb-5 py-10">
<NuxtLink class="text-3xl font-bold" to='https://www.facebook.com/KaliEnsemble'>KALI - performing ensemble</NuxtLink>
<div class="grid grid-cols-[20%,70%] p-5">
<nuxt-img src="/hdp_images/kali.jpg"/>
<div class="px-5">
Kali is a new music ensemble based in the Hague. They primarily work with composers with whom they can collaborate and experiment over long periods. They aim to develop an artistic practice unique to their relationship with their collaborators. Over the past years, they have formed close and active relationships with several composers based in The Hague and abroad realizing many large-scale projects with great attention to detail.
</div>
</div>
</div>
<div class="mb-5 py-10">
<NuxtLink class="text-3xl font-bold" to='https://users.utu.fi/jkari/'>Jarkko Kari - mathematician | invited guest</NuxtLink>
<div class="grid grid-cols-[20%,70%] p-5">
<nuxt-img class="w-full" src="/hdp_images/jarkko.jpg"/>
<div class="px-5">
Jarkko Kari received his MSc and PhD degrees in mathematics from the University of Turku in Finland in 1986 and 1990, respectively. He then worked for the Academy of Finland, and for Iterated Systems Inc. and the University of Iowa in the USA. Since year 2000 he has been a professor of mathematics at the University of Turku. His research interests include automata theory and the theory of computation, with emphasis on cellular automata, tilings and symbolic dynamics. Jarkko Kari has supervised twelve PhD theses, published over one hundred peer reviewed research articles and edited twenty conference proceedings and special issues on these topics. He serves in the editorial boards of eight scientific journals, and is currently a co-editor-in-chief of the journal Natural Computing. Jarkko Kari is a member of the Finnish Academy of Science and Letters since 2014.
</div>
</div>
</div>
<div class="mb-5 py-10">
<NuxtLink class="text-3xl font-bold" to='https://www.mathematik.hu-berlin.de/de/forschung/forschungsgebiete/mathematische-physik/borot-mp-homepage'>Gaëtan Borot - mathematician | organizer | moderator</NuxtLink>
<div class="grid grid-cols-[20%,70%] p-5">
<nuxt-img class="w-full" src="/hdp_images/gaetan.jpg"/>
<div class="px-5">
Gaëtan Borot was trained at École Normale Supérieure (Paris) in theoretical physicist and progressively moved to pure mathematics. He received his PhD from Universite d'Orsay / CEA Saclay in 2011. After a postdoctorate in Geneva and a visiting scholarship at MIT, he worked as a Group Leader at the Max Planck Institute for Mathematics in Bonn. Since 2020, he holds a bridge professorship between the Institute of Mathematics and the Institute of Physics of the Humboldt University of Berlin. He has worked on enumerative geometry, combinatorics, random matrix theory and mathematical aspects of quantum field theory, and likes to investigate the unexpected relations between seemingly different problems. He is also interested in scientific outreach.
</div>
</div>
</div>
</div>
</span>
</SwiperSlide>
<SwiperSlide data-hash="media" class="p-20 text-xl overflow-hidden">
<div class="flex justify-center">
<iframe src="https://player.vimeo.com/video/375784136" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen ></iframe>
</div>
</SwiperSlide>
<SwiperSlide data-hash="contributors" class="p-10 text-xl overflow-hidden">
<div class="max-h-[calc(100vh-27rem)] overflow-auto">
<div class="grid grid-cols-5 p-5 items-center">
<NuxtLink class="px-3" to='https://www.hu-berlin.de/en'><nuxt-img class="w-[100px]" src="/hdp_images/hu_logo.png"/></NuxtLink>
<NuxtLink class="px-3" to='https://www.km28.de/'><nuxt-img class="w-[100px]" src="/hdp_images/km28_logo.png"/></NuxtLink>
<NuxtLink class="px-3" to='https://www.ims-chips.com/'><nuxt-img class="w-[250px]" src="/hdp_images/ims_chips_logo.png"/></NuxtLink>
<NuxtLink class="px-3" to='https://www.akademie-solitude.de/'><nuxt-img class="w-[100px]" src="/hdp_images/akademie_schloss_solitude_logo.png"/></NuxtLink>
<NuxtLink class="px-3" to='https://mathplus.de/'><nuxt-img class="w-[200px]" src="/hdp_images/mathplus_logo_gray.png"/></NuxtLink>
</div>
</div>
</SwiperSlide>
<SwiperSlide data-hash="resources" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="overflow-auto">
<div class="mb-5 text-2xl font-bold">
a few selected articles:
</div>
<div class="mb-5">
Hao Wang (1961), Proving theorems by pattern recognitionII, Bell System Technical Journal, Volume: 40, Issue: 1.
</div>
<div class="mb-5">
Robert Berger (1966), The undecidability of the domino problem, American Mathematical Society, Volume 1, 1966.
</div>
<div class="mb-5">
Jarkko Kari (1996), A small aperiodic set of Wang tiles, Discrete Mathematics, Volume 160.
</div>
<div class="mb-5">
Emmanuel Jeandel and Michael Rao, An aperiodic set of 11 Wang tiles, Advances in Combinatorics, Volume 1.
</div>
<br>
<div class="mb-5 text-2xl font-bold">
a definitive book on tilings and patterns:
</div>
<div class="mb-5">
Branko Grunbaum and G.C. Shephard, Tilings and Patterns, Dover Books (originally published 1986)
</div>
<br>
<div class="mb-5 text-2xl font-bold">
a few useful links:
</div>
<div class="mb-5">
<NuxtLink to='https://grahamshawcross.com/2012/10/12/aperiodic-tiling/'>https://grahamshawcross.com/2012/10/12/aperiodic-tiling/</NuxtLink>
</div>
<div class="mb-5">
<NuxtLink to='https://grahamshawcross.com/2012/10/12/wang-tiles-and-aperiodic-tiling/'>https://grahamshawcross.com/2012/10/12/wang-tiles-and-aperiodic-tiling/</NuxtLink>
</div>
<div class="mb-5">
<NuxtLink to='https://en.wikipedia.org/wiki/Wang_tile'>https://en.wikipedia.org/wiki/Wang_tile</NuxtLink>
</div>
<div class="mb-5">
<NuxtLink to='https://en.wikipedia.org/wiki/Aperiodic_tiling'>https://en.wikipedia.org/wiki/Aperiodic_tiling</NuxtLink>
</div>
</div>
</span>
</SwiperSlide>
<SwiperSlide data-hash="contact" class="p-10 text-xl overflow-hidden">
<span class="swiper-no-swiping">
<div class="overflow-auto">
<div class="mb-5 text-2xl">
For information or any inquiries email Michael Winter by clicking
<NuxtLink class="inline-flex p-1" to='javascript:location="mailto:\u006d\u0077\u0069\u006e\u0074\u0065\u0072\u0040\u0075\u006e\u0062\u006f\u0075\u006e\u0064\u0065\u0064\u0070\u0072\u0065\u0073\u0073\u002e\u006f\u0072\u0067";void 0'>
<span class="font-bold">HERE</span>
</NuxtLink>
</div>
<div class="mb-5 text-2xl">
To download the poster for the project, click
<NuxtLink class="inline-flex p-1" to='https://unboundedpress.org/pubs/hdp_poster.pdf'>
<span class="font-bold">HERE</span>
</NuxtLink>
</div>
<div class="mb-5 text-2xl">
To download the poster specifically for the exhibition, click
<NuxtLink class="inline-flex p-1" to='https://unboundedpress.org/pubs/a_few_thoughts_exhibition_poster.pdf'>
<span class="font-bold">HERE</span>
</NuxtLink>
</div>
</div>
</span>
</SwiperSlide>
</Swiper>
</div>
</template>
<script>
import hdp_background from "assets/hdp_background.png";
export default {
data() {
return {
image: hdp_background, };
}
};
</script>
<script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore"
const audioPlayerStore = useAudioPlayerStore()
audioPlayerStore.setSoundCloudTrackID(324252345)
</script>

@ -1,100 +0,0 @@
<template>
<div class="bg-zinc-100 rounded-lg m-5 grid grid-cols-[60%,35%] gap-10 divide-x divide-solid divide-black py-4 min-h-[calc(100vh-10.5rem)]">
<div class="px-5">
<p class="text-lg">about</p>
<div class="leading-tight py-2 ml-3 text-sm">
<div class="leading-tight py-2">
My practice as a composer and sound artist is diverse, ranging from music created by digital and acoustic instruments to installations and kinetic sculptures. Each piece typically explores one simple process and often reflects various related interests of mine such as phenomenology, mathematics, epistemology, algorithmic information theory, and the history of science. To me, everything we experience is computable. Given this digital philosophy, I acknowledge even my most open works as algorithmic; and, while not always apparent on the surface of any given piece, the considerations of computability and epistemology are integral to my practice. I often reconcile epistemological limits with artistic practicality by considering and addressing the limits of computation from an artistic and experiential vantage point and by collaborating with other artists, mathematicians, and scientists in order to integrate objects, ideas, and texts from various domains as structural elements in my pieces. My work also aims to subvert discriminatory conventions and hierarchies by exploring alternative forms of presentation and interaction, often with minimal resources and low information.
</div>
<div class="leading-tight py-2">
My work has been presented at venues and festivals throughout the world such as REDCAT, in Los Angeles; the Ostrava Festival of New Music in the Czech Republic; Tsonami Arte Sonoro Festival in Valparaiso, Chile; the Huddersfield New Music Festival in the United Kingdom; and Umbral Sesiones at the Museo de Arte Contemporáneo in Oaxaca, Mexico. Recordings of my music have been released by XI Records, Another Timbre, New World Records, Edition Wandelweiser, Bahn Mi Verlag, Tsonami Records, and Pogus Productions. In 2008, I co-founded <em>the wulf.</em>, a Los Angeles-based organization dedicated to experimental performance and art. From 2018 to 2019, I was a fellow / artist-in-residence at the Akademie Schloss Solitude in Stuttgart, Germany. I currently reside in Berlin.
</div>
<br>
<br>
<div id="mc_embed_signup">
<form action="https://unboundedpress.us12.list-manage.com/subscribe/post?u=bdadd25738fedf704641f3a80&amp;id=01c5761ebb&amp;f_id=00f143e0f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self">
<label for="mce-EMAIL">subscribe to my mailing list to know about upcoming events</label>
<input id="mce-EMAIL" type="email" value="" name="EMAIL" placeholder="email address" required="" class="email">
<div style="position: absolute; left: -5000px;" aria-hidden="true">
<input type="text" name="b_bdadd25738fedf704641f3a80_01c5761ebb" tabindex="-1" value="">
</div>
<div id="mce-responses" class="clear foot">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div class="clear">
<input id="mc-embedded-subscribe" type="submit" value="subscribe" name="subscribe" class="button">
</div>
</form>
</div>
<br>
<br>
<div class="inline-flex place-items-center p-2">
Contact
<div>
<IconButton :visible="true" type="email" work="placeholder" link="javascript:location='mailto:\u006d\u0077\u0069\u006e\u0074\u0065\u0072\u0040\u0075\u006e\u0062\u006f\u0075\u006e\u0064\u0065\u0064\u0070\u0072\u0065\u0073\u0073\u002e\u006f\u0072\u0067';void 0"></IconButton>
</div>
</div>
<br>
<div class="inline-flex place-items-center p-2">
CV
<div>
<IconButton :visible="true" type="document" work="placeholder" link="https://unboundedpress.org/legacy/cv"></IconButton>
</div>
</div>
<br>
<div class="inline-flex place-items-center p-2">
Works List with Presentation History
<div>
<IconButton :visible="true" type="document" work="placeholder" link="https://unboundedpress.org/legacy/works_list"></IconButton>
</div>
</div>
<br>
<br>
</div>
</div>
<div class="px-5">
<ImageSlider bucket="images" :gallery="gallery" class="max-w-[90%]"></ImageSlider>
</div>
</div>
</template>
<script setup>
const { data: images } = await useFetch('https://unboundedpress.org/api/images.files?pagesize=200')
const { data: gallery } = await useFetch('https://unboundedpress.org/api/my_image_gallery?pagesize=200', {
transform: (gallery) => {
for (const item of gallery) {
item.image_id = images.value.find(obj => {return obj.filename === item.image})._id.$oid
}
return gallery //.sort((a,b) => a.priority - b.priority)
}
})
useHead({
titleTemplate: 'Michael Winter - About - Short Bio, Contact, CV, Works List, and Mailing List'
})
</script>
<style>
#mc_embed_signup form {text-align:left; padding:2px 0 2px 0;}
.mc-field-group { display: inline-block; } /* positions input field horizontally */
#mc_embed_signup input.email {border: 1px solid #ABB0B2; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; color: #343434; background-color: #fff; box-sizing:border-box; height:32px; padding: 0px 0.4em; display: inline-block; margin: 0; width:350px; vertical-align:top;}
#mc_embed_signup label {display:block; padding-bottom:10px;}
#mc_embed_signup .clear {display: inline-block;} /* positions button horizontally in line with input */
#mc_embed_signup .button {font-size: 13px; border: none; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; letter-spacing: .03em; color: #fff; background-color: #aaa; box-sizing:border-box; height:32px; line-height:32px; padding:0 18px; display: inline-block; margin: 0; transition: all 0.23s ease-in-out 0s;}
#mc_embed_signup .button:hover {background-color:#777; cursor:pointer;}
#mc_embed_signup div#mce-responses {float:left; top:-1.4em; padding:0em .5em 0em .5em; overflow:hidden; width:90%;margin: 0 5%; clear: both;}
#mc_embed_signup div.response {margin:1em 0; padding:1em .5em .5em 0; font-weight:bold; float:left; top:-1.5em; z-index:1; width:80%;}
#mc_embed_signup #mce-error-response {display:none;}
#mc_embed_signup #mce-success-response {color:#529214; display:none;}
#mc_embed_signup label.error {display:block; float:none; width:auto; margin-left:1.05em; text-align:left; padding:.5em 0;}
@media (max-width: 768px) {
#mc_embed_signup input.email {width:100%; margin-bottom:5px;}
#mc_embed_signup .clear {display: block; width: 100% }
#mc_embed_signup .button {width: 100%; margin:0; }
}
#mc_embed_signup{clear:left; width:100%;}
</style>

@ -1,92 +0,0 @@
<template>
<div class="bg-zinc-100 rounded-lg m-5 grid grid-cols-2 gap-10 divide-x divide-solid divide-black py-4 mb-10">
<div class="px-5">
<p class="text-lg">performances</p>
<div v-for="(item, index) in events">
<Collapsible title='placeholder' :modelValue='index <= 10' class="leading-tight py-2 ml-3 text-sm">
<template v-slot:title>
<div class="gap-1 w-[95%] px-2">
<div>
{{ item.formatted_date }}: {{item.venue.city}}, {{item.venue.state}}
<div class="ml-4 text-[#7F7F7F]">
{{ item.venue.name }}
</div>
</div>
</div>
</template>
<template v-slot:content>
<div v-for="performance in item.program">
<div class="italic text-sm ml-16 pt-1">{{performance.work}}</div>
<div v-if="performance.ensemble" class="ml-20">
{{ performance.ensemble }}
</div>
<div v-for="performer in performance.performers" class="ml-20">
{{ performer.name }} -
<span v-for="(instrument, index) in performer.instrument_tags">
<span v-if="index !== 0">, </span>
{{ instrument }}
</span>
</div>
</div>
<div class="italic text-sm ml-16 pt-1">{{item.legacy_program}}</div>
<div class="ml-20">{{item.legacy_performers}}</div>
</template>
</Collapsible>
</div>
</div>
<div class="px-5">
<p class="text-lg">lectures</p>
<div class="leading-tight py-2 ml-3 text-sm" v-for="item in lectures">
<div class="gap-1">
<div>
{{ item.formatted_date }}: {{item.location}}
<div v-for="talk in item.talks" class="ml-4 text-[#7F7F7F]">
{{ talk.title }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
const { data: events } = await useFetch('https://unboundedpress.org/api/events?pagesize=200', {
transform: (events) => {
for (const event of events) {
let date = new Date(event.start_date.$date)
event.formatted_date = ("0" + (date.getMonth() + 1)).slice(-2) + "." + ("0" + date.getDate()).slice(-2) + "." + date.getFullYear()
}
return events.sort((a,b) => b.start_date.$date - a.start_date.$date)
}
})
const { data: lectures } = await useFetch('https://unboundedpress.org/api/talks?pagesize=200', {
transform: (events) => {
for (const event of events) {
let date = new Date(event.date)
event.date = date
event.formatted_date = ("0" + (date.getMonth() + 1)).slice(-2) + "." + ("0" + date.getDate()).slice(-2) + "." + date.getFullYear()
if(typeof event.title === 'string' || event.title instanceof String) {event.talks = [{'title': event.title}]
} else {
let talks = []
for(const talk of event.title){
talks.push({"title": talk})
}
event.talks = talks
}
}
return events.sort((a,b) => b.date - a.date)
}
})
useHead({
titleTemplate: 'Michael Winter - Events - Performances and Lectures'
})
</script>

@ -1,198 +0,0 @@
<template>
<div class="bg-zinc-100 rounded-lg m-5 grid grid-cols-3 gap-10 divide-x divide-solid divide-black py-4 mb-10">
<div class="px-5">
<p class="text-lg">pieces</p>
<div class="py-2 ml-3" v-for="item in works">
<p class="font-thin">{{ item.year }}</p>
<div class="leading-tight py-1 ml-3" v-for="work in item.works">
<div class="grid grid-cols-[65%,30%] gap-1 font-thin">
<div class="italic text-sm">{{ work.title }}</div>
<div class="inline-flex">
<div>
<IconButton :visible="work.score" type="score" :work="work" :link="work.score" class="inline-flex p-1"></IconButton>
</div>
<div>
<IconButton :visible="work.soundcloud_trackid" type="audio" :work="work" class="inline-flex p-1"></IconButton>
</div>
<div>
<IconButton :visible="work.vimeo_trackid" type="video" :work="work" class="inline-flex p-1"></IconButton>
</div>
<div>
<IconButton :visible="work.gallery" type="image" :work="work" class="inline-flex p-1"></IconButton>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="px-5">
<p class="text-lg">writings</p>
<div class="leading-tight py-2 ml-3 text-sm" v-for="item in pubs">
<div class="grid grid-cols-[95%,5%] gap-1">
<div>
<span v-html="item.entryTags.title"></span>
<div class="ml-4 text-[#7F7F7F]">
{{ item.entryTags.author }}
<span v-if=item.entryTags.booktitle>{{ item.entryTags.booktitle}}.&nbsp;</span>
<span v-if=item.entryTags.journal>{{item.entryTags.journal}}.&nbsp;</span>
<span v-if=item.entryTags.editor>editors {{item.entryTags.editor}}&nbsp;</span>
<span v-if=item.entryTags.volume>volume {{item.entryTags.volume}}.</span>
<span v-if=item.entryTags.publisher>{{item.entryTags.publisher}}.</span>
{{ item.entryTags.year }}.
</div>
</div>
<div>
<IconButton :visible=item.entryTags.howpublished type="document" :link="item.entryTags.howpublished" class="inline-flex p-1"></IconButton>
</div>
</div>
</div>
</div>
<div class="px-5">
<p class="text-lg">albums</p>
<div class="leading-tight py-4 ml-3 text-sm" v-for="item in releases">
<p class="text-center leading-tight py-2">{{ item.title }}</p>
<button @click="modalStore.setModalProps('image', 'aspect-auto', true, 'album_art', [{image_id: item.album_art_id}], '')">
<nuxt-img :src="'https://unboundedpress.org/api/album_art.files/' + item.album_art_id + '/binary'"
quality="50"/>
</button>
<div class="flex place-content-center place-items-center">
<IconButton :visible="item.discogs_id" type="discogs" :link="'https://www.discogs.com/release/' + item.discogs_id"></IconButton>
<IconButton :visible="item.buy_link" type="buy" :link="item.buy_link"></IconButton>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useModalStore } from "@/stores/ModalStore"
const modalStore = useModalStore()
const groupBy = (x,f)=>x.reduce((a,b,i)=>((a[f(b,i,x)]||=[]).push(b),a),{});
const isValidUrl = urlString => {
/*
var urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i'); // validate fragment locator
return !!urlPattern.test(urlString);
*/
var pattern = /^((http|https|ftp):\/\/)/;
return pattern.test(urlString)
}
const { data: images } = await useFetch('https://unboundedpress.org/api/images.files?pagesize=200')
const { data: works } = await useFetch('https://unboundedpress.org/api/works?pagesize=200', {
transform: (works) => {
for (const work of works) {
if(work.score){
work.score = "/scores/" + work.score
}
/*
if(work.images){
let image_ids = [];
for (const image of work.images){
image_ids.push(images.value.find(obj => {return obj.filename === image.filename})._id.$oid)
}
work.image_ids = image_ids
}
*/
if(work.images){
let gallery = [];
for (const image of work.images){
gallery.push({
image_id: images.value.find(obj => {return obj.filename === image.filename})._id.$oid,
})
}
work.gallery = gallery
}
}
let priorityGroups = groupBy(works, work => work.priority)
let groups = groupBy(priorityGroups["1"], work => new Date(work.date.$date).getFullYear())
groups = Object.keys(groups).map((year) => {
return {
year,
works: groups[year].sort((a,b) => b.date.$date - a.date.$date)
};
});
groups.sort((a,b) => b.year - a.year)
groups.push({year: "miscellany", works: priorityGroups["2"].sort((a,b) => b.date.$date - a.date.$date)})
return groups
}
})
//const { data: pubs } = await useFetch('https://unboundedpress.org/api/publications/_aggrs/publications?pagesize=200')
//const { data: pubs } = await useFetch('https://unboundedpress.org/api/publications?sort=-entryTags.year&pagesize=200')
const { data: pubs } = await useFetch('https://unboundedpress.org/api/publications?pagesize=200', {
transform: (pubs) => {
for (const pub of pubs) {
if(pub.entryTags.howpublished && !(isValidUrl(pub.entryTags.howpublished))){
pub.entryTags.howpublished = "/pubs/" + pub.entryTags.howpublished
}
}
return pubs.sort((a,b) => (a.citationKey > b.citationKey) ? -1 : ((b.citationKey > a.citationKey) ? 1 : 0))
/*
return pubs.sort((a,b) => {
let aPrime = 5000
let bPrime = 5000
if(a.entryTags.year === 'forthcoming'){aPrime = 5000} else {aPrime = a.entryTags.year}
if(b.entryTags.year === 'forthcoming'){bPrime = 5000} else {bPrime = b.entryTags.year}
return bPrime - aPrime
})
*/
}
})
const { data: album_art } = await useFetch('https://unboundedpress.org/api/album_art.files?pagesize=200')
const { data: releases } = await useFetch('https://unboundedpress.org/api/releases?pagesize=200', {
//lazy: true,
//server: false,
transform: (releases) => {
for (const release of releases) {
release.album_art_id = album_art.value.find(obj => {return obj.filename === release.album_art})._id.$oid
}
return releases.sort((a,b) => b.date - a.date)
}
})
/*
watch(releases, (response)=>{
//console.log(response)
for (const item of response) {
useFetch(`https://unboundedpress.org/api/album_art.files?filter={"filename":"${item.album_art}"}`).then((response) => {
item.album_art_id = response.data.value[0]._id.$oid
})
}
return response
}, {
//deep: true,
immediate: true
})
*/
useHead({
titleTemplate: 'Michael Winter - Home / Works - Pieces, Publications, and Albums'
})
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

@ -1,3 +0,0 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

@ -1,15 +0,0 @@
import {defineStore} from "pinia";
export const useAudioPlayerStore = defineStore("AudioPlayerStore", {
state: () => ({"soundcloud_trackid": "1032587794"}),
actions: {
setSoundCloudTrackID(trackid) {
if (typeof trackid !== 'undefined') {
this.soundcloud_trackid = trackid
}
},
clearSoundCloudTrackID() {
this.soundcloud_trackid = 'undefined'
}
}
})

@ -1,15 +0,0 @@
import {defineStore} from "pinia";
export const useModalStore = defineStore("ModalStore", {
state: () => ({"type": "", "aspect":"", "isOpen":false, "bucket":"", "gallery":"", "vimeo_trackid":""}),
actions: {
setModalProps(type, aspect, isOpen, bucket, gallery, vimeo_trackid) {
this.type = type
this.aspect = aspect
this.isOpen = isOpen
this.bucket = bucket
this.gallery = gallery
this.vimeo_trackid = vimeo_trackid
}
}
})

@ -1,4 +0,0 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

@ -62,8 +62,7 @@ app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser()); app.use(bodyParser());
app.use(cookieParser()); app.use(cookieParser());
// change first argument here to be on subdirectory app.use(express.static(path.join(__dirname, 'public')));
app.use('/legacy', express.static(path.join(__dirname, 'public')));
// Make our db accessible to our router // Make our db accessible to our router
app.use(function(req,res,next){ app.use(function(req,res,next){
@ -71,8 +70,7 @@ app.use(function(req,res,next){
next(); next();
}); });
// change first argument here to be on subdirectory app.use('/', routes);
app.use('/legacy/', routes);
// catch 404 and forward to error handler // catch 404 and forward to error handler
app.use(function(req, res, next) { app.use(function(req, res, next) {

@ -1,15 +1,3 @@
@article{Winter23c,
author = {with Abrahão, F., Zenil, H., Porto, F., Wehmuth, K. and D'Ottaviano, I.},
title = {A simplicity bubble problem in formal-theoretic learning systems},
year = {forthcoming},
}
@article{Winter23b,
author = {with Abrahão, F., Cavassane, R. and D'Ottaviano, I.},
title = {The simplicity bubble effect as a zemblanitous phenomenon in learning systems},
year = {forthcoming},
}
@article{Winter20c, @article{Winter20c,
author = {with Dantas, P.}, author = {with Dantas, P.},
title = {Evolving curricula: reflections on The Quarantine Seminars}, title = {Evolving curricula: reflections on The Quarantine Seminars},
@ -17,24 +5,24 @@ year = {forthcoming},
howpublished = {reflections_on_the_quarantine_seminars.pdf} howpublished = {reflections_on_the_quarantine_seminars.pdf}
} }
@incollection{Winter20b, @misc{Winter20b,
author = {},
title = {Liner notes to the album "the yggdrasil-soli" by Ulrich Krieger},
publisher = {Winds Measures Recordings},
year = {2020},
howpublished = {krieger_yggdrasil_soli_liner_notes.pdf}
}
@incollection{Winter20a,
author = {}, author = {},
title = {Meta+phenomenology: primer towards a phenomenology formally based on algorithmic information theory and metabiology}, title = {Meta+phenomenology: primer towards a phenomenology formally based on algorithmic information theory and metabiology},
editor = {Doria, F. and Wuppuluri, S.}, editor = {Doria, F. & Wuppuluri, S.},
booktitle = {Unravelling Complexity: Life and Work of Gregory Chaitin}, booktitle = {Unravelling Complexity: Life and Work of Gregory Chaitin},
publisher = {World Scientific}, publisher = {World Scientific},
year = {2020}, year = {2020},
howpublished = {https://www.worldscientific.com/worldscibooks/10.1142/11270} howpublished = {https://www.worldscientific.com/worldscibooks/10.1142/11270}
} }
@misc{Winter20a,
author = {},
title = {Liner notes to the album <em>the yggdrasil-soli</em> by Ulrich Krieger},
publisher = {Winds Measures Recordings},
year = {2020},
howpublished = {krieger_yggdrasil_soli_liner_notes.pdf}
}
@incollection{Winter19c, @incollection{Winter19c,
author = {}, author = {},
title = {A few more thoughts about Leibniz: the prediction of harmonic distance in harmonic space (with text to preliminary thoughts)}, title = {A few more thoughts about Leibniz: the prediction of harmonic distance in harmonic space (with text to preliminary thoughts)},
@ -58,13 +46,13 @@ howpublished = {reflections_on_the_quarantine_seminars.pdf}
@misc{Winter19a, @misc{Winter19a,
author = {with Polansky, L.}, author = {with Polansky, L.},
title = {liner notes to the album <em>Changes</em> by James Tenney}, title = {liner notes to the album <em>"Changes"</em> by James Tenney},
publisher = {New World Records}, publisher = {New World Records},
year = {2019}, year = {2019},
howpublished = {tenney_changes_liner_notes.pdf} howpublished = {tenney_changes_liner_notes.pdf}
} }
@incollection{Winter17a, @incollection{Winter17,
author = {}, author = {},
title = {On minimal change musical morphologies}, title = {On minimal change musical morphologies},
editor = {Pareyon, G., Pina-Romero, S., Agustin-Aquino, O.A., and Lluis-Puebla, E.}, editor = {Pareyon, G., Pina-Romero, S., Agustin-Aquino, O.A., and Lluis-Puebla, E.},
@ -75,15 +63,15 @@ howpublished = {reflections_on_the_quarantine_seminars.pdf}
} }
@misc{Winter16a, @misc{Winter16,
author = {}, author = {},
title = {Liner notes to the album <em>Three Pieces for Two Pianos</em> by Larry Polansky}, title = {Liner notes to the album "Three Pieces for Two Pianos" by Larry Polansky},
publisher = {New World Records}, publisher = {New World Records},
year = {2016}, year = {2016},
howpublished = {polansky_piano_liner_notes.pdf} howpublished = {polansky_piano_liner_notes.pdf}
} }
@Book{Winter15a, @Book{Tenney15,
author = {Tenney, J.}, author = {Tenney, J.},
title = {From Scratch: Writings in Music Theory}, title = {From Scratch: Writings in Music Theory},
editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.}, editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
@ -92,7 +80,7 @@ editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
howpublished = {https://www.press.uillinois.edu/books/catalog/78det5km.html} howpublished = {https://www.press.uillinois.edu/books/catalog/78det5km.html}
} }
@misc{Winter14b, @misc{grove,
author = {}, author = {},
title = {Approximating Omega}, title = {Approximating Omega},
journal = {Carbono (online)}, journal = {Carbono (online)},
@ -103,7 +91,7 @@ editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
@article{Winter14a, @article{Winter10,
author = {with Akhmedov, A.}, author = {with Akhmedov, A.},
title = {Chordal and timbral morphologies using Hamiltonian cycles}, title = {Chordal and timbral morphologies using Hamiltonian cycles},
journal = {Journal of Mathematics and Music}, journal = {Journal of Mathematics and Music},
@ -114,10 +102,10 @@ year = {2014},
howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf} howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf}
} }
@incollection{Winter12a, @incollection{Winter12,
author = {}, author = {},
title = {Relativity and scalability with respect to sound and silence}, title = {Relativity and scalability with respect to sound and silence},
editor = {Lely, J. and Saunders, M.}, editor = {Lely, J. & Saunders, M.},
booktitle = {Word Events: Perspectives on Verbal Notation}, booktitle = {Word Events: Perspectives on Verbal Notation},
publisher = {Bloomsbury}, publisher = {Bloomsbury},
year = {2012}, year = {2012},
@ -125,8 +113,8 @@ howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf}
} }
@article{Winter11a, @article{Polansky11,
author = {with Polansky, L. and Barnett, A.}, author = {with Polansky, L. & Barnett, A.},
title = {A few more words about James Tenney: dissonant counterpoint and statistical feedback}, title = {A few more words about James Tenney: dissonant counterpoint and statistical feedback},
journal = {Journal of Mathematics and Music}, journal = {Journal of Mathematics and Music},
volume = {5}, volume = {5},
@ -136,7 +124,7 @@ year = {2011},
howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf} howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf}
} }
@misc{Winter10d, @misc{emy,
author = {}, author = {},
title = {Notes on a new economics for a new music}, title = {Notes on a new economics for a new music},
journal = {Experimental Music Yearbook (online)}, journal = {Experimental Music Yearbook (online)},
@ -145,7 +133,7 @@ howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf}
howpublished = {https://experimentalmusicyearbook.com/filter/Michael-Winter/notes-on-a-new-economics-for-a-new-art} howpublished = {https://experimentalmusicyearbook.com/filter/Michael-Winter/notes-on-a-new-economics-for-a-new-art}
} }
@article{Winter10c, @article{doi:10.1080/07494467.2010.509594,
author = {with Barrett, G.D.}, author = {with Barrett, G.D.},
title = {LiveScore: realtime notation in the music of Harris Wulfson}, title = {LiveScore: realtime notation in the music of Harris Wulfson},
journal = {Contemporary Music Review}, journal = {Contemporary Music Review},
@ -158,7 +146,7 @@ howpublished = {Livescore.pdf}
@phdthesis{Winter10b, @phdthesis{WinterDiss10,
type = {Dissertation}, type = {Dissertation},
title = {Structural Metrics: an epistemology}, title = {Structural Metrics: an epistemology},
author = {}, author = {},
@ -169,7 +157,7 @@ howpublished = {Livescore.pdf}
} }
@article{Winter10a, @article{SAM:7914368,
author = {}, author = {},
title = {James Tenney: Selected Works 19611969 (review)}, title = {James Tenney: Selected Works 19611969 (review)},
journal = {Journal of the Society for American Music}, journal = {Journal of the Society for American Music},
@ -180,7 +168,7 @@ year = {2010},
pages = {531533} pages = {531533}
} }
@misc{Winter09a, @misc{grove,
author = {with Hanson, S., Streb, C., and Polansky, L.}, author = {with Hanson, S., Streb, C., and Polansky, L.},
title = {James Tenney biographical entry}, title = {James Tenney biographical entry},
booktitle = {Grove Dictionary of American Music}, booktitle = {Grove Dictionary of American Music},
@ -188,9 +176,9 @@ pages = {531533}
year = {2009} year = {2009}
} }
@article{Winter08a, @article{doi:10.1080/07494460701671566,
author = {}, author = {},
title = {On James Tenney's <em>Arbor Vitae</em> for string quartet}, title = {On James Tenney's Arbor Vitae for string quartet},
journal = {Contemporary Music Review}, journal = {Contemporary Music Review},
volume = {27}, volume = {27},
number = {1}, number = {1},
@ -199,7 +187,7 @@ year = {2008},
howpublished = {On_Arbor_Vitae.pdf} howpublished = {On_Arbor_Vitae.pdf}
} }
@article{Winter07b, @article{Winter07,
author = {}, author = {},
title = {Mavericks on mavericks: James Tenneys last courses at CalArts}, title = {Mavericks on mavericks: James Tenneys last courses at CalArts},
journal = {MusikTexte}, journal = {MusikTexte},
@ -211,8 +199,8 @@ howpublished = {https://musiktexte.de/epages/dc91cfee-4fdc-41fe-82da-0c2b88528c1
} }
@inproceedings{Winter07a, @inproceedings{Wulfson:2007,
author = {with Barrett, G.D. and Wulfson, H.}, author = {with Barrett, G.D. & Wulfson, H.},
title = {Automatic Notation Generators}, title = {Automatic Notation Generators},
booktitle = {Proceedings of the 7th International NIME Conference}, booktitle = {Proceedings of the 7th International NIME Conference},
series = {NIME '07}, series = {NIME '07},

@ -1,15 +1,3 @@
@article{Winter23c,
author = {with Abrahão, F., Zenil, H., Porto, F., Wehmuth, K. and D'Ottaviano, I.},
title = {A simplicity bubble problem in formal-theoretic learning systems},
year = {forthcoming},
}
@article{Winter23b,
author = {with Abrahão, F., Cavassane, R. and D'Ottaviano, I.},
title = {The simplicity bubble effect as a zemblanitous phenomenon in learning systems},
year = {forthcoming},
}
@article{Winter20c, @article{Winter20c,
author = {with Dantas, P.}, author = {with Dantas, P.},
title = {Evolving curricula: reflections on The Quarantine Seminars}, title = {Evolving curricula: reflections on The Quarantine Seminars},
@ -17,24 +5,24 @@ year = {forthcoming},
howpublished = {reflections_on_the_quarantine_seminars.pdf} howpublished = {reflections_on_the_quarantine_seminars.pdf}
} }
@incollection{Winter20b, @misc{Winter20b,
author = {},
title = {Liner notes to the album <em>the yggdrasil-soli</em> by Ulrich Krieger},
publisher = {Winds Measures Recordings},
year = {2020},
howpublished = {krieger_yggdrasil_soli_liner_notes.pdf}
}
@incollection{Winter20a,
author = {}, author = {},
title = {Meta+phenomenology: primer towards a phenomenology formally based on algorithmic information theory and metabiology}, title = {Meta+phenomenology: primer towards a phenomenology formally based on algorithmic information theory and metabiology},
editor = {Doria, F. and Wuppuluri, S.}, editor = {Doria, F. & Wuppuluri, S.},
booktitle = {Unravelling Complexity: Life and Work of Gregory Chaitin}, booktitle = {Unravelling Complexity: Life and Work of Gregory Chaitin},
publisher = {World Scientific}, publisher = {World Scientific},
year = {2020}, year = {2020},
howpublished = {https://www.worldscientific.com/worldscibooks/10.1142/11270} howpublished = {https://www.worldscientific.com/worldscibooks/10.1142/11270}
} }
@misc{Winter20a,
author = {},
title = {Liner notes to the album <em>the yggdrasil-soli</em> by Ulrich Krieger},
publisher = {Winds Measures Recordings},
year = {2020},
howpublished = {krieger_yggdrasil_soli_liner_notes.pdf}
}
@incollection{Winter19c, @incollection{Winter19c,
author = {}, author = {},
title = {A few more thoughts about Leibniz: the prediction of harmonic distance in harmonic space (with text to preliminary thoughts)}, title = {A few more thoughts about Leibniz: the prediction of harmonic distance in harmonic space (with text to preliminary thoughts)},
@ -64,7 +52,7 @@ howpublished = {reflections_on_the_quarantine_seminars.pdf}
howpublished = {tenney_changes_liner_notes.pdf} howpublished = {tenney_changes_liner_notes.pdf}
} }
@incollection{Winter17a, @incollection{Winter17,
author = {}, author = {},
title = {On minimal change musical morphologies}, title = {On minimal change musical morphologies},
editor = {Pareyon, G., Pina-Romero, S., Agustin-Aquino, O.A., and Lluis-Puebla, E.}, editor = {Pareyon, G., Pina-Romero, S., Agustin-Aquino, O.A., and Lluis-Puebla, E.},
@ -75,7 +63,7 @@ howpublished = {reflections_on_the_quarantine_seminars.pdf}
} }
@misc{Winter16a, @misc{Winter16,
author = {}, author = {},
title = {Liner notes to the album <em>Three Pieces for Two Pianos</em> by Larry Polansky}, title = {Liner notes to the album <em>Three Pieces for Two Pianos</em> by Larry Polansky},
publisher = {New World Records}, publisher = {New World Records},
@ -83,7 +71,7 @@ howpublished = {reflections_on_the_quarantine_seminars.pdf}
howpublished = {polansky_piano_liner_notes.pdf} howpublished = {polansky_piano_liner_notes.pdf}
} }
@Book{Winter15a, @Book{Tenney15,
author = {Tenney, J.}, author = {Tenney, J.},
title = {From Scratch: Writings in Music Theory}, title = {From Scratch: Writings in Music Theory},
editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.}, editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
@ -92,7 +80,7 @@ editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
howpublished = {https://www.press.uillinois.edu/books/catalog/78det5km.html} howpublished = {https://www.press.uillinois.edu/books/catalog/78det5km.html}
} }
@misc{Winter14b, @misc{grove,
author = {}, author = {},
title = {Approximating Omega}, title = {Approximating Omega},
journal = {Carbono (online)}, journal = {Carbono (online)},
@ -103,7 +91,7 @@ editor = {Polansky, L., Pratt, L., Wannamaker, R., and Winter, M.},
@article{Winter14a, @article{Winter10,
author = {with Akhmedov, A.}, author = {with Akhmedov, A.},
title = {Chordal and timbral morphologies using Hamiltonian cycles}, title = {Chordal and timbral morphologies using Hamiltonian cycles},
journal = {Journal of Mathematics and Music}, journal = {Journal of Mathematics and Music},
@ -114,10 +102,10 @@ year = {2014},
howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf} howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf}
} }
@incollection{Winter12a, @incollection{Winter12,
author = {}, author = {},
title = {Relativity and scalability with respect to sound and silence}, title = {Relativity and scalability with respect to sound and silence},
editor = {Lely, J. and Saunders, M.}, editor = {Lely, J. & Saunders, M.},
booktitle = {Word Events: Perspectives on Verbal Notation}, booktitle = {Word Events: Perspectives on Verbal Notation},
publisher = {Bloomsbury}, publisher = {Bloomsbury},
year = {2012}, year = {2012},
@ -125,8 +113,8 @@ howpublished = {Chordal_and_timbral_morphologies_using_Hamiltonian_cycles.pdf}
} }
@article{Winter11a, @article{Polansky11,
author = {with Polansky, L. and Barnett, A.}, author = {with Polansky, L. & Barnett, A.},
title = {A few more words about James Tenney: dissonant counterpoint and statistical feedback}, title = {A few more words about James Tenney: dissonant counterpoint and statistical feedback},
journal = {Journal of Mathematics and Music}, journal = {Journal of Mathematics and Music},
volume = {5}, volume = {5},
@ -136,7 +124,7 @@ year = {2011},
howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf} howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf}
} }
@misc{Winter10d, @misc{emy,
author = {}, author = {},
title = {Notes on a new economics for a new music}, title = {Notes on a new economics for a new music},
journal = {Experimental Music Yearbook (online)}, journal = {Experimental Music Yearbook (online)},
@ -145,7 +133,7 @@ howpublished = {Dissonant_counterpoint_and_statistical_feedback.pdf}
howpublished = {https://experimentalmusicyearbook.com/filter/Michael-Winter/notes-on-a-new-economics-for-a-new-art} howpublished = {https://experimentalmusicyearbook.com/filter/Michael-Winter/notes-on-a-new-economics-for-a-new-art}
} }
@article{Winter10c, @article{doi:10.1080/07494467.2010.509594,
author = {with Barrett, G.D.}, author = {with Barrett, G.D.},
title = {LiveScore: realtime notation in the music of Harris Wulfson}, title = {LiveScore: realtime notation in the music of Harris Wulfson},
journal = {Contemporary Music Review}, journal = {Contemporary Music Review},
@ -158,7 +146,7 @@ howpublished = {Livescore.pdf}
@phdthesis{Winter10b, @phdthesis{WinterDiss10,
type = {Dissertation}, type = {Dissertation},
title = {Structural Metrics: an epistemology}, title = {Structural Metrics: an epistemology},
author = {}, author = {},
@ -169,7 +157,7 @@ howpublished = {Livescore.pdf}
} }
@article{Winter10a, @article{SAM:7914368,
author = {}, author = {},
title = {James Tenney: Selected Works 19611969 (review)}, title = {James Tenney: Selected Works 19611969 (review)},
journal = {Journal of the Society for American Music}, journal = {Journal of the Society for American Music},
@ -180,7 +168,7 @@ year = {2010},
pages = {531533} pages = {531533}
} }
@misc{Winter09a, @misc{grove,
author = {with Hanson, S., Streb, C., and Polansky, L.}, author = {with Hanson, S., Streb, C., and Polansky, L.},
title = {James Tenney biographical entry}, title = {James Tenney biographical entry},
booktitle = {Grove Dictionary of American Music}, booktitle = {Grove Dictionary of American Music},
@ -188,7 +176,7 @@ pages = {531533}
year = {2009} year = {2009}
} }
@article{Winter078a, @article{doi:10.1080/07494460701671566,
author = {}, author = {},
title = {On James Tenney's <em>Arbor Vitae</em> for string quartet}, title = {On James Tenney's <em>Arbor Vitae</em> for string quartet},
journal = {Contemporary Music Review}, journal = {Contemporary Music Review},
@ -199,7 +187,7 @@ year = {2008},
howpublished = {On_Arbor_Vitae.pdf} howpublished = {On_Arbor_Vitae.pdf}
} }
@article{Winter07b, @article{Winter07,
author = {}, author = {},
title = {Mavericks on mavericks: James Tenneys last courses at CalArts}, title = {Mavericks on mavericks: James Tenneys last courses at CalArts},
journal = {MusikTexte}, journal = {MusikTexte},
@ -211,8 +199,8 @@ howpublished = {https://musiktexte.de/epages/dc91cfee-4fdc-41fe-82da-0c2b88528c1
} }
@inproceedings{Winter07a, @inproceedings{Wulfson:2007,
author = {with Barrett, G.D. and Wulfson, H.}, author = {with Barrett, G.D. & Wulfson, H.},
title = {Automatic Notation Generators}, title = {Automatic Notation Generators},
booktitle = {Proceedings of the 7th International NIME Conference}, booktitle = {Proceedings of the 7th International NIME Conference},
series = {NIME '07}, series = {NIME '07},

@ -18,13 +18,9 @@ $(document).ready(function() {
populateAbout(); populateAbout();
populateGallerySelector(); populateGallerySelector();
// I am not sure why I was changing the url here, but for legacy more I am taking this out
/*
if (window.location.href.split('/').pop().substring(0,3) != "#lg") { if (window.location.href.split('/').pop().substring(0,3) != "#lg") {
window.history.replaceState("object or string", "Title", "/"); window.history.replaceState("object or string", "Title", "/");
} }
*/
} }
$( window ).resize(function() { $( window ).resize(function() {
@ -867,11 +863,11 @@ function populatePublications() {
var wlButton = $("<button id=cv_button>").attr({title: "Works List with Presentation History"}).addClass('score_icon'); var wlButton = $("<button id=cv_button>").attr({title: "Works List with Presentation History"}).addClass('score_icon');
cvButton.click(function() { cvButton.click(function() {
window.open(window.location.href + 'cv'); window.open('/cv');
}); });
wlButton.click(function() { wlButton.click(function() {
window.open(window.location.href + 'works_list'); window.open('/works_list');
}); });
/* /*

@ -126,10 +126,6 @@ router.get('/works_list', function(req, res, next) {
if( (titleToSearch.indexOf('one') !== -1) && (titleToSearch.indexOf('two') !== -1)){ if( (titleToSearch.indexOf('one') !== -1) && (titleToSearch.indexOf('two') !== -1)){
titleToSearch = 'two'; titleToSearch = 'two';
} }
if( (titleToSearch == 'berger-knuth') || (titleToSearch == 'robinson') || (titleToSearch == 'penrose') || (titleToSearch == 'ammann') || (titleToSearch == 'kari-culik') || (titleToSearch == 'jaendel-rao')){
titleToSearch = 'a history of the domino problem';
}
// bug: why is mercado san juan or first not updating? // bug: why is mercado san juan or first not updating?
db.collection('events').find( { $text: { $search: "\"" + titleToSearch + "\"" } } ).toArray(function (err, events1) { db.collection('events').find( { $text: { $search: "\"" + titleToSearch + "\"" } } ).toArray(function (err, events1) {
db.collection('events').find( { 'program' : { "$elemMatch" : { 'work' : { $regex : titleToSearch.replace('(','\\(').replace(')','\\)'), $options : 'i' } } } } ).toArray(function (err, events2) { db.collection('events').find( { 'program' : { "$elemMatch" : { 'work' : { $regex : titleToSearch.replace('(','\\(').replace(')','\\)'), $options : 'i' } } } } ).toArray(function (err, events2) {

@ -6,7 +6,7 @@ html
link(rel='stylesheet', href='lightslider/css/lightslider.css') link(rel='stylesheet', href='lightslider/css/lightslider.css')
link(rel='stylesheet', href='lightgallery/css/lightgallery.css') link(rel='stylesheet', href='lightgallery/css/lightgallery.css')
link(rel='stylesheet', href='stylesheets/style.css') link(rel='stylesheet', href='/stylesheets/style.css')
link(rel='stylesheet', href='//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css') link(rel='stylesheet', href='//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css')
@ -17,8 +17,8 @@ html
script(src='//code.jquery.com/jquery-2.1.4.min.js') script(src='//code.jquery.com/jquery-2.1.4.min.js')
script(src='//code.jquery.com/ui/1.11.4/jquery-ui.min.js') script(src='//code.jquery.com/ui/1.11.4/jquery-ui.min.js')
script(src='javascripts/global.js') script(src='/javascripts/global.js')
script(src='javascripts/file.js') script(src='/javascripts/file.js')
//script(src="http://bibtex-js.googlecode.com/svn/trunk/src/bibtex_js.js") //script(src="http://bibtex-js.googlecode.com/svn/trunk/src/bibtex_js.js")
@ -29,7 +29,7 @@ html
script(src="lightgallery/js/lightgallery.js") script(src="lightgallery/js/lightgallery.js")
script(src="lightgallery/js/lg-hash.js") script(src="lightgallery/js/lg-hash.js")
script(src="javascripts/pluralize.js") script(src="/javascripts/pluralize.js")
script. script.

Loading…
Cancel
Save