diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/assets/nuweb/mdlio-app.css b/assets/nuweb/mdlio-app.css new file mode 100755 index 0000000..8e25c0a --- /dev/null +++ b/assets/nuweb/mdlio-app.css @@ -0,0 +1,332 @@ +@font-face { + font-family: "Material Design Icons"; + src: url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.eot?v=1.9.32"); + src: url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.eot?#iefix&v=1.9.32") format("embedded-opentype"), url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.woff2?v=1.9.32") format("woff2"), url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.woff?v=1.9.32") format("woff"), url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.ttf?v=1.9.32") format("truetype"), url("https://cdn.materialdesignicons.com/1.9.32/fonts/materialdesignicons-webfont.svg?v=1.9.32#materialdesigniconsregular") format("svg"); + font-weight: normal; + font-style: normal; +} + +.item .mdl-card_title { + padding: 8px; + width: calc( 100% - 16px ); +} +.item .mdl-card__title-text { + text-decoration:none; + font-family: "Roboto","Helvetica","Arial",sans-serif +} + + +.item.type-image.thumb .card-no-media { + display:none; +} + +.item.type-image.thumb:hover .card-no-media { + display:block; +} + +.item.type-person.thumb .card-title-wrapper { + height:0px; + position:relative; + overflow:visible; +} + + +.item .card-title-body { + width: 100%; + + +} + +.item.type-person.thumb .card-title-body, +.item.type-image.thumb .card-title-body { + background: rgba(0,0,0,0.25); + color: #fff; + text-shadow: 0px 0px 7px rgba(0, 0, 0, 1); + bottom: 0px; + position: absolute; +} + +.item.type-image.thumb .card-title-body { + display:none; +} + +.item.type-person:hover .card-title-body, +.item.type-image:hover .card-title-body { + display: block; +} + +input.material-icons[type="submit"] { + font-family: "Material Icons"; +} + +.file .mdl-card_title-text { + word-break:break-all; +} + + +.file .title:before { + font-family: "Material Design Icons"; + margin-right:8px; + content: "\f214"; +} + +.file.type-directory .title:before { + content: "\f24b"; +} + +.file.type-person .title:before { + content: "\f004"; +} + +.file.type-image .title:before { + + content: "\f21f"; +} + +.file.type-video .title:before { + content: "\f22b"; +} + +.file[data-mime="application/pdf"] .title:before { + content: "\f225" +} + + + +.item .mdl-card.as-dialog { + position:fixed; + display:block; + top:100px; + /* max-width: 960px; */ + width: calc( 100% - 140px); + height: calc( 100% - 140px); + z-index:6; +} + +.item .mdl-card.as-dialog:before { + position:fixed; + display:block; + top:0px; + left:0px; + background:#000; + width:10%; + height:10%; + z-index:5; + content:""; +} + + + + + +.selection-bar { + display:block !important; + opacity:0; + transform: translateY(-70px); +} + + +.selection-bar, +.selection-bar .mdl-layout__drawer-button { + color:rgba(0,0,0,.87); +} + +.selection-mode .selection-bar { + display:block; + transform: translateY(0px); + opacity:1; +} + +.selection-mode .selectable .action.select, +.selectable:hover .action.select { + display:block; + opacity:1; +} + +.selectable>.action.select { + position:absolute; + left:-8px; + top:-12px; + background: #fff; + border-radius: 50%; + width:24px; + height:24px; + min-width:24px; + box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); + z-index: 2; + + /*display:none;*/ + opacity:0; +} + +.selection-bar, +.selectable .action.select { + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.item .mdl-card__actions { + display:none; +} + +.mdl-card.upgraded .mdl-card__actions { + display:block; +} + + + + +.action.pin { + /* position:absolute; */ + /* top:0px; */ + /* right: 8px; */ +} + +.action.pin:before { + font-family: "Material Design Icons"; + content: "\f403"; + line-height:24px; +} + + +.item .mdl-card__actions { + text-align:right; + visibility: hidden; + opacity:0; + transition: transform 0.3s ease, opacity 0.3s ease; + +} + +.item .action.select i.material-icons { + font-size:14px; +} + +.item.selected { + background:#555; + /*outline: 1px solid #555;*/ + box-shadow: 0 0 8px rgba(0,0,0,0.87); +} + + + +.item:hover .mdl-card__actions { + visibility: visible; + opacity:1; +} + + +.item.selected .action.select { + background: #555; + color: #fff; + display:block; +} + +.item .mdl-button--icon { + min-width:32px; +} + +.gallery { + width: calc(100% - 16px); + padding: 0px; +} + +.breadcrumb { + margin-top: 24px; + display:block; +} +.breadcrumb + h1 { + margin-top:0px; +} + + +.gallery .item.mdl-card { + min-height: auto; +} + +.gallery .item .mdl-card_media img { + width:100%; + height:auto; +} + +.gallery .item.no-thumb .mdl-card_media a { + display: block; + /*min-height: 100px;*/ + width:100%; + background: rgba(255,255,255,0.25); + text-decoration:none; + color:#fff; + vertical-align: middle; + text-align: center; +} + +.item .mdl-card { + width:100%; + min-height:48px; +} + +/* +.gallery .item.no-thumb.type-directory .mdl-card_media a::before { + content: "folder"; + font-family: "Material Icons"; + margin-right:8px; + font-size:128px; + line-height:128px; + text-decoration:none; + +} +*/ + +.main-width { + max-width: 960px; + margin: auto; + position: relative; +} + +@media (max-width: 976px) { + .main-width { + margin:0 8px; + } +} + + +#scooter { + position: absolute; + overflow: hidden; + top:0px; + width:100%; + bottom:0px; + height:0px; +} + +#sizer { + background:rgba(0,0,0,0.10); + position:relative; + height:100%; +} + +#sizer .gutter { + background:rgba(0,0,0,0.1); + height:100%; +} + +/* Layout Hack */ + +.mdl-layout { + overflow: visible; +} +.mdl-layout__drawer { + position: fixed; +} +.mdl-layout__content { + display: block; + overflow: visible; + margin-top: 64px; +} +.is-small-screen .mdl-layout__content { + margin-top: 56px; +} +.mdl-layout__header { + position: fixed; +} +.mdl-layout__obfuscator { + position: fixed; +} diff --git a/assets/nuweb/mdlio-app.js b/assets/nuweb/mdlio-app.js new file mode 100755 index 0000000..1f7aed0 --- /dev/null +++ b/assets/nuweb/mdlio-app.js @@ -0,0 +1,444 @@ +window.mdlio = { + + outlayer: function(outlayer) { + this.outlayers = []; + if (outlayer) { + if (Array.isArray(outlayer)) { + for (var e of outlayer) { + this.outlayers.push(e); + } + } else { + this.outlayers.push(outlayer); + } + } + this.add = function(outlayer) { + this.outlayers.push(outlayer); + } + ; + this.appended = function(items) { + mdlio.upgrade.appended(items); + for (outlayer of this.outlayers) { + outlayer.appended(items); + } + }; + }, + cards: { + upgrade: function(element) { + element.querySelectorAll(".mdl-card").forEach(mdlio.cards.upgradeCard) + }, + upgradeCard: function(card) { + console.log(card); + + if(card.querySelector(".mdl-card_title")) { + card.classList.add("has-title"); + } + if(card.querySelector(".mdl-card__actions")) { + card.classList.add("has-actions"); + } + + const parent=card.parentNode; + const as=card.querySelector(".action.select"); + + /*if(parent.matches(".card-wrapper") && as) { + parent.appendChild(as); + }*/ + + card.classList.remove("upgradeable"); + card.classList.add("upgraded"); + + }, + }, + lightbox: function(grid, selector, options) { + var pswpElement = document.querySelectorAll('.pswp')[0]; + if (pswpElement == null ) { + pswpElement = mdlio.addPSWP(); + } + /*this.box = $(grid).find(selector).simpleLightbox(options); */ + var items = []; + this.items = items; + const linkClicked = function(e) { + console.log(this, this.lboxId); + e.preventDefault(); + var options = { + index: this.lboxId + }; + var gallery = new PhotoSwipe(pswpElement,PhotoSwipeUI_Default,items,options); + gallery.init(); + }; + this.appended = function(appendedItems) { + //this.box.destroy(); + for (var item of appendedItems) { + var link = item.querySelector(selector); + if (link == null ) { + continue; + } + var img = link.querySelector('img'); + var width = img.naturalWidth; + var height = img.naturalHeight; + var maybeSize = link.getAttribute('data-img-size'); + if (maybeSize) { + console.log(maybeSize); + [ width , height ] = maybeSize.split('x').map(function(x) { + return Number.parseInt(x); + }); + } + var slide = { + msrc: img.getAttribute('src'), + src: link.getAttribute('href'), + w: width, + h: height + }; + console.log(slide); + var slideId = items.push(slide) - 1; + var links = item.querySelectorAll(selector); + for (link of links) { + if (link.getAttribute('href') == slide.src) { + link.lboxId = slideId; + link.addEventListener("click", linkClicked); + } + } + } + //this.box = $(grid).find(selector).simpleLightbox(options); + } + }, + infiniteScroll: function(grid, item, nextPage, outlayer) { + var outlayers = new mdlio.outlayer(outlayer); + var infScroll = new InfiniteScroll(grid,{ + path: nextPage, + hideNav: nextPage, + append: item, + outlayer: outlayers, + status: '.page-load-status', + scrollThresold: 100, + prefill: true //elementScroll: '.mdl-layout__content', + }); + infScroll.outlayers = outlayers; + return infScroll; + }, + cardsGallery: function(grid, item, lightboxItem) { + const lightbox = new mdlio.lightbox(grid,lightboxItem + " a"); + const msnry = new Masonry(grid,{ + itemSelector: 'none', + // select none at first + columnWidth: document.querySelector('#gutter'), + gutter: 0, + containerStyle: { + width: 'calc(100% - 16px)', + padding: '0px', + position: 'relative' + }, + percentPosition: true, + // nicer reveal transition + visibleStyle: { + transform: 'translateY(0)', + opacity: 1 + }, + hiddenStyle: { + transform: 'translateY(100px)', + opacity: 0 + }, + }); + this.lightbox = lightbox; + this.masonry = msnry; + imagesLoaded(grid, function() { + grid.classList.remove('are-images-unloaded'); + msnry.options.itemSelector = item; + var items = grid.querySelectorAll(item); + lightbox.appended(items); + msnry.appended(items); + }); + if (document.querySelector(".next-page")) { + this.infiniteScroll = new mdlio.infiniteScroll(grid,item,'.next-page',[msnry, this.lightbox]); + } + }, + addPSWP: function() { + var body = document.querySelector('body'); + var pswp = document.createElement('div'); + pswp.classList.add('pswp'); + pswp.innerHTML = ` +
+ +
+ +
+
+
+
+
+ +
+ +
+ +
+ + + + + + + + + + + +
+
+
+
+
+
+
+
+ + + + + + + +
+
+
+ +
+ +
` + body.appendChild(pswp); + return pswp; + }, + forms: { + upgrade: function(element) { + element.querySelectorAll('form .async[type="submit"]').forEach(function(button) { + button.addEventListener("click", function(e) { + e.preventDefault(); + const form = button.findParentBySelector("form"); + $.ajax({ + type: "POST", + cache: false, + url: form.action, + data: $(form).serialize(), + success: function(msg) { + console.log("Success", msg); + } + }); + }); + }); + } + }, + upgrade: { + initial: function() { + mdlio.upgrade.appended([document.querySelector("body")]); + }, + + appended: function(items) { + items.forEach(mdlio.upgrade.upgrade); + }, + + upgrade: function(item) { + for(const upgrader of mdlio.upgrade.upgraders) { + upgrader.upgrade(item); + } + }, + + upgraders: [], + }, + selectable: new (function(){ + this.availableActions = new Set(); + + const selected = new Set(); + + this.selected = selected; + + const actionButtons = {}; + + this.displayToolbar = function() { + var toolbar; + if(!this.toolbar) { + toolbar = document.createElement("div"); + toolbar.classList.add("selection-bar"); + toolbar.classList.add("mdl-layout__header"); + toolbar.classList.add("mdl-color--white"); + const header = document.querySelector("header.mdl-layout__header"); + toolbar.innerHTML=` +
+ + 0 Selected +
+
+
+ `; + + + header.parentNode.appendChild(toolbar); + toolbar.querySelector("button.cancel").addEventListener("click",this.stopSelection); + + this.toolbar = { + toolbar: toolbar, + counter: toolbar.querySelector(".mdl-layout_title span"), + actions: toolbar.querySelector(".actions") + }; + } + toolbar = this.toolbar; + toolbar.counter.innerHTML = this.selected.size + }; + + this.addToolbarAction = function(action) { + this.availableActions.add(action); + var button = actionButtons[action]; + if(!button) { + button = document.createElement("div"); + button.innerHTML='' + button.classList.add("mdl-button"); + button.classList.add("mdl-button--icon"); + button.addEventListener("click", this.actionClickHandler); + button.dataset.action = action; + actionButtons[action] = button; + + } + this.toolbar.actions.appendChild(button); + + }; + + this.removeToolbarAction = function(action) { + this.availableActions.delete(action); + var button = actionButtons[action]; + if (button) { + this.toolbar.actions.removeChild(button) + } + } + + this.startSelection = function() { + this.displayToolbar(); + document.querySelector("body").classList.add("selection-mode"); + + } + + this.stopSelection = function() { + document.querySelector("body").classList.remove("selection-mode"); + for (item of mdlio.selectable.selected) { + mdlio.selectable.unselect(item); + } + } + + this.select = function(item) { + item.classList.add("selected"); + this.selected.add(item); + + this.startSelection(); + + var actions = item.querySelectorAll("[data-selectable-action]"); + + var actionsSet = new Set(); + for (const action of actions) { + actionsSet.add(action.dataset.selectableAction); + } + console.log("actions", this.availableActions); + + if(this.selected.size == 1) { + for(action of actionsSet) { + this.addToolbarAction(action); + } + } else { + for (const action of this.availableActions) { + if(!actionsSet.has(action)) { + this.removeToolbarAction(action); + } + } + } + + }; + this.unselect = function(item) { + item.classList.remove("selected"); + this.selected.delete(item); + + if(mdlio.selectable.selected.size == 0) { + this.stopSelection(); + } + }; + + const actionsHandlers = {} + this.action = function(key, fn) { + actionsHandlers[key] = function(items) {items.forEach(fn)}; + }; + + this.batchAction = function(key, fn) { + actionsHandlers[key] = fn; + }; + + this.actionClickHandler = function(ev) { + var action = this.dataset.action; + console.log("Invoking action", action, ev); + var handler = actionsHandlers[action]; + if(handler) { + handler(Array.from(selected)); + } + + + } + + this.upgrade = function(item) { + var items=[]; + if (item.matches(".selectable")) { + items=[item]; + } else { + items=item.querySelectorAll(".selectable") + } + + items.forEach(function(item) { + + var selectButton = item.querySelector('.action.select'); + if(!selectButton) { + selectButton = document.createElement("button"); + selectButton.classList.add("mdl-button","mdl-button--icon"); + selectButton.classList.add("action","select"); + selectButton.innerHTML='check'; + item.appendChild(selectButton); + } + + selectButton.addEventListener("click", function(e) { + e.preventDefault(); + if(item.classList.contains("selected")) { + mdlio.selectable.unselect(item); + } else { + mdlio.selectable.select(item); + } + + }); + + }) + + }; + + })() +}; + +mdlio.upgrade.upgraders.push(mdlio.cards); +mdlio.upgrade.upgraders.push(mdlio.forms); +mdlio.upgrade.upgraders.push(mdlio.selectable); + + +Element.prototype.findParentBySelector = function(selector) { + console.log(this); + var cur = this.parentNode; + while (cur && !cur.matches(selector)) { + //keep going up until you find a match + cur = cur.parentNode; + //go up + } + return cur; + //will return null if not found +} +, +window.addEventListener('load', function() { + mdlio.upgrade.initial(); +}); +window.addEventListener('load', function() { + console.log("Content-loaded"); +}); diff --git a/bin/nuweb-run b/bin/nuweb-run index 35ed7bd..3895726 100755 --- a/bin/nuweb-run +++ b/bin/nuweb-run @@ -1,15 +1,31 @@ #!/usr/bin/env bash +nuweb_start=$(date +%s.%N) +nuweb_stop() { + stop=$(date +%s.%N) + nux.log info Finished: "${REQUEST_URI} $(echo "$stop - $nuweb_start" | bc)" +} +trap nuweb_stop EXIT + + readonly NUWEB_BIN_DIR=$(dirname $(realpath ${BASH_SOURCE[0]})) -source $NUWEB_BIN_DIR/../inc/nux-base.inc.sh +source $NUWEB_BIN_DIR/../inc/nux.inc.sh + + + nux.use nuweb +umask 0002 dirty.url.decode() { sed -e "s/%20/ /gi" <<< "$@" } + nux.log.level debug -NUWEB_SCRIPT_URI="${SCRIPT_FILENAME#$DOCUMENT_ROOT}" \ -NUWEB_MAYBE_PWD="$DOCUMENT_ROOT$(dirty.url.decode "${REQUEST_URI%%\?*}")" \ -NUWEB_REQUEST_PATH="${REQUEST_URI%%\?*}" \ - source "$1" + +DOCUMENT_ROOT=${DOCUMENT_ROOT%/} +NUWEB_SCRIPT_URI="${SCRIPT_FILENAME#$DOCUMENT_ROOT}" +NUWEB_SCRIPT_DIR_URI="${NUWEB_SCRIPT_URI%/*}" +NUWEB_MAYBE_PWD="$DOCUMENT_ROOT$(dirty.url.decode "${REQUEST_URI%%\?*}")" +NUWEB_REQUEST_PATH="${REQUEST_URI%%\?*}" +nux.dsl.exec nux.nuxsh.language.def "$1" diff --git a/bin/nweb-old b/bin/nweb-old new file mode 100755 index 0000000..3baa03b --- /dev/null +++ b/bin/nweb-old @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +echo Content-Type: text/html +echo +echo + +env +exit 0 + + +readonly NUWEB_BIN_DIR=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $NUWEB_BIN_DIR/../inc/nux.inc.sh + +nux.use nuweb +umask 0002 + +dirty.url.decode() { + sed -e "s/%20/ /gi" <<< "$@" +} +nux.log.level debug + +NUWEB_SCRIPT_URI="${SCRIPT_FILENAME#$DOCUMENT_ROOT}" \ +NUWEB_MAYBE_PWD="$DOCUMENT_ROOT$(dirty.url.decode "${REQUEST_URI%%\?*}")" \ +NUWEB_REQUEST_PATH="${REQUEST_URI%%\?*}" \ + + + + + +nux.dsl.exec nux.nuxsh.language.def "$1" diff --git a/bin/nweb-run b/bin/nweb-run deleted file mode 120000 index acf4251..0000000 --- a/bin/nweb-run +++ /dev/null @@ -1 +0,0 @@ -nuweb-run \ No newline at end of file diff --git a/dev/exclude.findbugs.xml b/dev/exclude.findbugs.xml old mode 100644 new mode 100755 diff --git a/dev/modified-guava-style.xml b/dev/modified-guava-style.xml old mode 100644 new mode 100755 diff --git a/gitignore-global b/gitignore-global old mode 100644 new mode 100755 diff --git a/inc/dsl/nux.fs.inc.sh b/inc/dsl/nux.fs.inc.sh old mode 100644 new mode 100755 diff --git a/inc/dsl/nuxfs.apply.dsl b/inc/dsl/nuxfs.apply.dsl old mode 100644 new mode 100755 diff --git a/inc/dsl/nuxfs.check.dsl b/inc/dsl/nuxfs.check.dsl old mode 100644 new mode 100755 diff --git a/inc/dsl/nuxfs.describe.dsl b/inc/dsl/nuxfs.describe.dsl old mode 100644 new mode 100755 diff --git a/inc/dsl/nuxfs.dsl b/inc/dsl/nuxfs.dsl old mode 100644 new mode 100755 diff --git a/inc/dsl/nuxfs.fix.dsl b/inc/dsl/nuxfs.fix.dsl old mode 100644 new mode 100755 diff --git a/inc/labely.inc.sh b/inc/labely.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nuweb.inc.sh b/inc/nuweb.inc.sh deleted file mode 100644 index 0c94692..0000000 --- a/inc/nuweb.inc.sh +++ /dev/null @@ -1,89 +0,0 @@ -## #nuweb - Server-side HTTP scripting library for BASH -## -## *nuweb* is set of BASH function to ease implementation of CGI scripts using -## BASH. Idea behind it, is to allow for reuse of existing shell scripts -## and command-line utilities, without need to duplicate functionality. -## -## *nuweb* is based on *nux-env*, which allows large code-share with *nux-env* -## command line tools and use of *nux-env* libraries. -## -## *nuweb* is separated into several components: -## -## nuweb:: -## this library, base functionality -## nuweb/router:: router support functionality - dispatch of code -## based on path and query parameters -## nuweb/html:: -## basic support for generating HTML content -## - -## #Public functions: - -## nuweb.status:: -## Sets HTTP response status code. -## NOTE: If any content was already returned this function does not work. -## -nuweb.status() { - local code=$1; - local message=$2; - echo "HTTP/1.1 $code $message" -} - -## nuweb.content_type:: -## Sets HTTP response content type -## NOTE: If any content was already returned this function does not work. -nuweb.content_type() { - echo "Content-Type: $@" -} - -## nuweb.redirect:: [--relative] -## -nuweb.redirect() { - local prefix=""; - if [ "$1" == "--relative" ]; then - shift; - prefix="$(dirname "$SCRIPT_NAME")/" - fi - - echo Location: "$prefix$@" - echo - echo -} - -## nuweb.redirect.exec:: -## -nuweb.redirect.exec() { - if [ "$1" == "--relative" ]; then - args="--relative"; - shift; - fi - fn=$1; shift; - nuweb.redirect $args $($fn $@) -} - -## nuweb.http.query.var:: [defaultValue] -## Reads HTTP query string from variable *QUERY_STRING*, parses it and returns -## value of parameter *param* or *defaultValue*. -## -## NOTE: Currently does not perform urldecode -nuweb.http.query.var() { - local to_read=$1; - local line_separrated=$(sed "s/&/\n/g" <<< $QUERY_STRING) - - while IFS="=" read -r var value; do - if [ "$var" = "$to_read" ]; then - echo "$value" - return; - fi - done <<< "$line_separrated" - echo $2 -} - -## nuweb.http.query.to_var:: -## -nuweb.http.query.to_var() { - local line_separrated=$(sed "s/&/\n/g" <<< $QUERY_STRING) - while IFS="=" read -r var value; do - declare -x "nuweb_QUERY_$var"="$value" - done <<< "$line_separrated" -} diff --git a/inc/nuweb.nuxsh.sh b/inc/nuweb.nuxsh.sh new file mode 100755 index 0000000..4818a4b --- /dev/null +++ b/inc/nuweb.nuxsh.sh @@ -0,0 +1,115 @@ +## #nuweb - Server-side HTTP scripting library for BASH +## +## *nuweb* is set of BASH function to ease implementation of CGI scripts using +## BASH. Idea behind it, is to allow for reuse of existing shell scripts +## and command-line utilities, without need to duplicate functionality. +## +## *nuweb* is based on *nux-env*, which allows large code-share with *nux-env* +## command line tools and use of *nux-env* libraries. +## +## *nuweb* is separated into several components: +## +## nuweb:: +## this library, base functionality +## nuweb/router:: router support functionality - dispatch of code +## based on path and query parameters +## nuweb/html:: +## basic support for generating HTML content +## + +## #Public functions: + +## nuweb.status:: +## Sets HTTP response status code. +## NOTE: If any content was already returned this function does not work. +## +@namespace nuweb { + function :status { + local code=$1;shift; + local message=$@; + echo "HTTP/1.1 $code $message" + } + +## nuweb.content_type:: +## Sets HTTP response content type +## NOTE: If any content was already returned this function does not work. + function :content_type { + echo "Content-Type: $@" + } + +## nuweb.redirect:: [--relative] +## + function :redirect { + local prefix=""; + if [ "$1" == "--relative" ]; then + shift; + prefix="$(dirname "$SCRIPT_NAME")/" + fi + nux.log debug "Redirecting to:" "$@" + if [ -z "$@" ]; then + :status 404 NOT FOUND + else + echo Location: "$prefix$@" + echo + echo + fi + } + +## nuweb.redirect.exec:: +## + function :redirect.exec { + if [ "$1" == "--relative" ]; then + args="--relative"; + shift; + fi + fn=$1; shift; + nuweb.redirect $args "$($fn $@)" + } + +## nuweb.http.query.var:: [defaultValue] +## Reads HTTP query string from variable *QUERY_STRING*, parses it and returns +## value of parameter *param* or *defaultValue*. +## +## NOTE: Currently does not perform urldecode +} + + +function nuweb.http.post.with_env fn { + if [ "${CONTENT_TYPE%;*}" = "application/x-www-form-urlencoded" ]; then + post_query=$(cat); + nux.log debug "POST Query: $post_query" + nuweb.http.query.with_env0 "$post_query" $fn "$@" + else + $fn "$@" + fi + +} + +@namespace nuweb.http.query { + function :var() { + local to_read=$1; + local line_separrated=$(sed "s/&/\n/g" <<< $QUERY_STRING) + + while IFS="=" read -r var value; do + if [ "$var" = "$to_read" ]; then + echo "$value" + return; + fi + done <<< "$line_separrated" + echo $2 +} + +## nuweb.http.query.to_var:: +## + function :with_env { + :with_env0 "$QUERY_STRING" "$@" + } + + function :with_env0 query fn { + nux.log debug "GET Query: $QUERY_STRING"; + while IFS="=" read -d "&" -r var value; do + declare -x "NUWEB_QUERY_${var//-/_}"="$(dirty.url.decode "$value")"; + done <<< "${query}&"; + $fn "$@" + } +} diff --git a/inc/nuweb/html.inc.sh b/inc/nuweb/html.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nuweb/html.nuxsh.syntax.nuxsh.sh b/inc/nuweb/html.nuxsh.syntax.nuxsh.sh new file mode 100755 index 0000000..1b8b03d --- /dev/null +++ b/inc/nuweb/html.nuxsh.syntax.nuxsh.sh @@ -0,0 +1,5 @@ +@namespace block.rewrite { + :call nuweb.html.html '+e html' '-e html' + :call nuweb.html.head '+e html' '-e html' + +} diff --git a/inc/nuweb/html.syntax.nuxsh.sh b/inc/nuweb/html.syntax.nuxsh.sh new file mode 100755 index 0000000..458e5b0 --- /dev/null +++ b/inc/nuweb/html.syntax.nuxsh.sh @@ -0,0 +1,69 @@ +@prefix statement nux.nuxsh.statement +@prefix block nux.nuxsh.block + +@namespace nuweb.html.syntax { + + function :statement { + :start "$@" + :end "$@" + } + + function :start tag indent args { + echo -n "${indent}echo '<$tag'"; + + nux.log debug "Args are '$args'"; + set -- $args + local attr=""; + local classes=""; + for arg; do + if [ -n "$attr" ] ; then + echo -n $arg"'\"'" + attr=""; + else + case "$arg" in + @*) + attr="${arg#@}"; + echo -n " '${attr}=\"'" + ;; + .*) classes="${classes}${arg//./ }";; + *) + nux.dsl.process.fail "unknown argument '$arg' '$line' " + return 1; + ;; + esac + fi + done + if [ -n "$classes" ] { + echo -n " 'class=\"'$classes'\"'" + } + echo " '>'"; + } + + function :end tag indent { + echo "${indent}echo ''"; + } + + function :element { + for tag in "$@" ; do + block:rewrite.call nuweb.html.dynamic.$tag "nuweb.html.element $tag" "nuweb.html.element.end $tag" + block:rewrite.func nuweb.html.$tag "nuweb.html.syntax.start $tag" "nuweb.html.syntax.end $tag" + statement:rewrite.func nuweb.html.$tag "nuweb.html.syntax.statement $tag" + statement:rewrite.call nuweb.dynamic.$tag "nuweb.html.element --close $tag" + + + done + } + + :element html body head title + :element style link meta script + :element header main nav + + :element span div p pre + :element a img + + :element b i strong small + + :element h1 h2 h3 h4 h5 h6 + + :element form input submit button textarea select label +} diff --git a/inc/nuweb/mdl.io.app.inc.sh b/inc/nuweb/mdl.io.app.inc.sh deleted file mode 100644 index edff8f5..0000000 --- a/inc/nuweb/mdl.io.app.inc.sh +++ /dev/null @@ -1,268 +0,0 @@ -nux.use nuweb/mdl.io -nux.use nuweb/router -nux.use nux.mime - -mdlio.app() { - mdlio.app.html "$@" -} - -app.header() { - : -} - -mdlio.app.custom() { - local func=$1; shift; - nux.exec.or $func app.$func "$@"; -} - -mdlio.app.html() { - nuweb.content_type text/html - echo - local spec="$1"; shift; - $spec "$@"; - - local appName=$(nux.exec.optional app.name); - local title="$(nux.exec.optional title)"; - echo "" - +e html - +e head - e meta @charset utf-8 - e meta @http-equiv x-ua-compatible @content ie=edge - e meta @name "viewport" @content "width=device-width, initial-scale=1.0, minimum-scale=1.0" - mdlio.css $(mdlio.app.custom color.primary) $(mdlio.app.custom color.accent) - e link @rel stylesheet @href "$NUWEB_SCRIPT_URI/action:asset/mdlio-app.css" - e link @rel stylesheet @href "https://unpkg.com/simplelightbox@1.11.0/dist/simplelightbox.css" - - nux.exec.optional app.custom.head - e title "$title - $appName" - -e head - +e body .mdl-base - +mdlio.layout .mdl-layout--fixed-header - e.mdlio.header++ "$appName" - mdlio.app.custom header - -e div - -e header - if nux.check.function app.drawer ; then - +e div .mdl-layout__drawer - app.drawer - nux.exec.optional drawer - -e div - fi - +mdlio.main - nux.exec.optional app.main.start - main "$@" - - - +e div @id scooter - +e div .mdl-grid @id sizer - e div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet - -e div - -e div - - nux.exec.optional app.main.end - -e main - -e div - mdlio.app.photoswipe.html - - e script @src https://code.jquery.com/jquery-3.2.1.min.js - e script @src https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.js - #e script @src https://unpkg.com/simplelightbox@1.11.0/dist/simple-lightbox.js - e script @src https://unpkg.com/photoswipe@4.1.2/dist/photoswipe.js - e script @src https://unpkg.com/photoswipe@4.1.2/dist/photoswipe-ui-default.js - e link @rel stylesheet @href https://unpkg.com/photoswipe@4.1.2/dist/photoswipe.css - e link @rel stylesheet @href https://unpkg.com/photoswipe@4.1.2/dist/default-skin/default-skin.css - e script @src https://unpkg.com/infinite-scroll@3/dist/infinite-scroll.pkgd.js - e script @src "$NUWEB_SCRIPT_URI/action:asset/mdlio-app.js" - e script @src "https://code.getmdl.io/1.3.0/material.min.js" - nux.exec.optional scripts - - -e body - -e html -} - -app.color.primary() { - echo teal -} - -app.color.accent() { - echo cyan -} - -mdlio.app.infinity() { - # outlayer: msnry, - local container=$1; - local item=$2; - local nextPage=$3; - local outlayer=""; - e script """ - var grid = document.querySelector('$container'); - var infScroll = new InfiniteScroll( grid, { - path: '$nextPage', - append: '$item', - $outlayer - status: '.page-load-status', - elementScroll: '.mdl-layout__content', - }); - """ - -} - -function mdlio.app.masonry() { - local grid="$1" - local gridItem="$2" - local lightBoxItem="$3" - local gutter="$4" - local nextPage="$5"; - e script """ - var gallery = new mdlio.cardsGallery(document.querySelector('$grid'), '$gridItem', '$lightBoxItem'); -""" - -} - - -function mdlio.app.thumb.uri() { - local filename="$1"; - local mimetype="$2"; - - #if [ -d "$filename" ] ; then - # nux.log info "File $filename is folder." - # filename=$(find "$1" -maxdepth 1 -iname "*.jpg" -or -iname "*.png" | head -n1) - # nux.log info "Using $filename for thumbnail." - #fi - - local thumb_name="$(thumby.name.shared "$filename")" - local dirname=$(dirname "$filename") - local thumb_path="$dirname/.sh_thumbnails/large/$thumb_name" - if [ -e "$thumb_path" ] ; then - nux.dirty.urlencode $thumb_path - elif thumby.thumb.can.generate "$filename" "$mimetype"; then - echo "$NUWEB_SCRIPT_URI/action:thumb/$NUWEB_REQUEST_PATH/$filename" - fi -} - -function mdlio.app.thumb.get() { - img_path="${@##/}" - nux.log info "Generating thumb for" $(pwd) "$img_path" - thumb_path=$(thumby.thumb.get "${DOCUMENT_ROOT}/$img_path"); - if [ -n "$thumb_path" ]; then - nux.dirty.urlencode ${thumb_path#$DOCUMENT_ROOT}; - fi -} - - -mdlio.app.run() { - nuweb.router.exec mdlio.app.routes -} - -mdlio.app.routes() { - get() { - local uri_spec="$1"; shift; - nuweb.get "$uri_spec" mdlio.app "$@" - } - - get.paginate() { - main() { - #nux.exec.optional before "$@"; - before=before after=after nuweb.paginate div .gallery .mdl-grid items per-item next-page 20; - #nux.exec.optional after "$@"; - } - next-page() { - +e div .mdl-grid - e div .mdl-cell .mdl-cell--11-col .mdl-cell--col-3-phone .mdl-cell--col-7-tablet - +e div .mdl-cell .mdl-cell--1-col - e a .next-page .mdl-button.mdl-button--colored @href "${REQUEST_URI%%?*}?page=$1&per_page=$2" Next - -e div - -e div - } - get "$@" - } - - #nuweb.get "/action:zip:serve/@+" mdlio.action.zip.serve - nuweb.get "/action:thumb/@+" nuweb.redirect.exec mdlio.app.thumb.get - nuweb.get "/action:asset/@" mdlio.app.asset - nux.exec.optional app.routes; -} - - -mdlio.app.asset() { - file="$NUX_ENV_DIR/assets/nuweb/$1"; - if [ -e "$file" ]; then - mime=$(nux.mime "$file"); - echo Content-Type: $mime - echo - cat "$file" - fi -} - - -mdlio.app.photoswipe.html() { - cat < - - -EOF -} diff --git a/inc/nuweb/mdl.io.inc.sh b/inc/nuweb/mdl.io.inc.sh deleted file mode 100644 index 84663e0..0000000 --- a/inc/nuweb/mdl.io.inc.sh +++ /dev/null @@ -1,25 +0,0 @@ - -nux.use nuweb/html - -mdlio.css() { - e.link stylesheet "https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" - e.link stylesheet "https://fonts.googleapis.com/icon?family=Material+Icons" - e.link stylesheet "https://code.getmdl.io/1.3.0/material.${1}-${2}.min.css" - -} - -e.mdlio.header++() { - +e header .mdl-layout__header .mdl-color--primary - +e div .mdl-layout__header-row - e span .mdl-layout-title $1 - e div .mdl-layout-spacer -} - - -e.alias mdlio.layout "div" ".mdl-layout .mdl-js-layout" -e.alias mdlio.main "main" ".mdl-layout__content" -e.alias mdlio.card "div" ".mdl-card.mdl-shadow--2dp" - -.mdl_cell() { - echo .mdl-cell.mdl-cell--"$1"-col.mdl-cell--"$2"-col-tablet.mdl-cell--"$2"-col-phone -} diff --git a/inc/nuweb/mdlio.app.defaults.nuxsh.sh b/inc/nuweb/mdlio.app.defaults.nuxsh.sh new file mode 100755 index 0000000..3007c04 --- /dev/null +++ b/inc/nuweb/mdlio.app.defaults.nuxsh.sh @@ -0,0 +1,16 @@ + +@namespace app { + + function :color.primary { + echo teal + } + + function :color.accent { + echo cyan + } + + function :header { + : + } + +} diff --git a/inc/nuweb/mdlio.app.nuxsh.sh b/inc/nuweb/mdlio.app.nuxsh.sh new file mode 100755 index 0000000..f0b6c5b --- /dev/null +++ b/inc/nuweb/mdlio.app.nuxsh.sh @@ -0,0 +1,228 @@ +@syntax nuweb/html + +nux.use nuweb/mdlio +nux.use nuweb/mdlio.app.defaults +nux.use nuweb/router +nux.use nux/mime +nux.use thumby +nux.use thumby/builtin + +@prefix h nuweb.html +@prefix router nuweb.router + +@block:rewrite:call2 e +e -e ; + +@namespace mdlio.app { + + function mdlio.app spec { + #nux.exec.optional app.init + $spec "$@"; + mdlio.app.html + } + + function :custom func { + nux.exec.or $func app.$func "$@"; + } + + function :additional-css css { + mdlio_app_additional_css="$mdlio_app_additional_css $css" + } + + function :additional-script script { + mdlio_app_additional_script="$mdlio_app_additional_script $script" + } + + function :html { + nuweb.content_type text/html + echo + + local app_uri="${NUWEB_SCRIPT_URI}"; + local appName=$(nux.exec.optional app.name); + echo "" + h:html { + h:head { + h:meta @charset utf-8 + h:meta @http-equiv x-ua-compatible @content ie=edge + h:meta @name "viewport" @content "width=device-width, initial-scale=1.0, minimum-scale=1.0" + mdlio.css $(mdlio.app.custom color.primary) $(mdlio.app.custom color.accent) + h:link @rel stylesheet @href "$NUWEB_SCRIPT_URI/action/mdlio:asset/mdlio-app.css" + h:link @rel stylesheet @href "https://unpkg.com/simplelightbox@1.11.0/dist/simplelightbox.css" + + for css in $mdlio_app_additional_css; do + h:link @rel stylesheet @href "$NUWEB_SCRIPT_DIR_URI/$css" + done + + nux.exec.optional app.custom.head + h:title { + nux.exec.optional title + echo - + nux.exec.optional app.name + } + } + h:body .mdl-base { + +mdlio.layout .mdl-layout--fixed-header + e.mdlio.header++ "$appName" + mdlio.app.custom header + -e div + -e header + if nux.check.function app.drawer ; then + h:div .mdl-layout__drawer { + app.drawer + nux.exec.optional drawer + } + fi + +mdlio.main + nux.exec.optional app.main.start + + nux.exec.or app.template-main app.content "$@" + + + + + -e main + -e div + + mdlio.app.photoswipe.html + + h:div .scripts { + h:script @src "https://code.jquery.com/jquery-3.2.1.min.js" + h:script @src "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.js" + h:script @src "https://unpkg.com/photoswipe@4.1.2/dist/photoswipe.js" + h:script @src "https://unpkg.com/photoswipe@4.1.2/dist/photoswipe-ui-default.js" + h:link @rel stylesheet @href https://unpkg.com/photoswipe@4.1.2/dist/photoswipe.css + h:link @rel stylesheet @href https://unpkg.com/photoswipe@4.1.2/dist/default-skin/default-skin.css + h:script @src "https://unpkg.com/infinite-scroll@3/dist/infinite-scroll.pkgd.js" + h:script @src "$(mdlio.app.action.uri mdlio:asset /mdlio-app.js)" + h:script @src "https://code.getmdl.io/1.3.0/material.min.js" + + for script in $mdlio_app_additional_script; do + h:script @src "$NUWEB_SCRIPT_DIR_URI/$script" + done + + nux.exec.optional scripts + } + } + } + } + + function :grid-sizer { + h:div @id scooter { + h:div .mdl-grid @id sizer { + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + h:div @id gutter .gutter .mdl-cell .mdl-cell--1-col .mdl-cell--1-col-phone .mdl_cell--1-col-tablet + } + } + } + + function :infinity container item nextPage { + local outlayer=""; + e script """ + var infScroll = new InfiniteScrolldocument.querySelector('$container'); grid, { + path: '$nextPage', + append: '$item', + $outlayer + status: '.page-load-status', + elementScroll: '.mdl-layout__content' + }); + """ +} + +function :masonry grid gridItem lightBoxItem gutter nextPage { + e script """ + var gallery = new mdlio.cardsGallery(document.querySelector('$grid'), '$gridItem', '$lightBoxItem'); + """ + +} + +function :action.uri action path args { + echo "$NUWEB_SCRIPT_URI/action/${action}${path}${args}" +} + +function :thumb.uri filename mimetype { + local thumb_name="$(thumby.name.shared "$filename")" + local dirname=$(dirname "$filename") + local thumb_path="$dirname/.sh_thumbnails/large/$thumb_name" + + if thumby.thumb.can.generate "$filename" "$mimetype"; then + if thumby.thumb.should.generate "$filename" "$mimetype"; then + :action.uri mdlio:thumb "$NUWEB_REQUEST_PATH/$filename" + return 0; + fi + fi + + + if [ -e "$thumb_path" ] ; then + nux.dirty.urlencode "$thumb_path" + fi +} + +function :thumb.get { + img_path="${@##/}" + nux.log info "Generating thumb for" $(pwd) "$img_path" + thumb_path="$(thumby.thumb.get "${DOCUMENT_ROOT}/$img_path")"; + if [ -n "$thumb_path" ]; then + nux.dirty.urlencode "${thumb_path#$DOCUMENT_ROOT}"; + fi +} + + +function :run { + nuweb.router.exec mdlio.app.routes +} + +function :routes { + function :post uri_spec { + router:post "$uri_spec" mdlio.app "$@" + } + + function :get uri_spec { + router:get "$uri_spec" mdlio.app "$@" + } + + function :get.paginate { + function app.content { + #nux.exec.optional before "$@"; + before=before after=after nuweb.paginate div .gallery .mdl-grid items per-item next-page 20; + #nux.exec.optional after "$@"; + } + function next-page { + h:div .mdl-grid { + h:div .mdl-cell .mdl-cell--11-col .mdl-cell--col-3-phone .mdl-cell--col-7-tablet + h:div .mdl-cell .mdl-cell--1-col { + h:a .next-page .mdl-button.mdl-button--colored @href "${REQUEST_URI%%?*}?page=$1&per_page=$2" Next + } + } + } + :get "$@" + } + + #nuweb.get "/action:zip:serve/@+" mdlio.action.zip.serve + router:get "/action/mdlio:thumb/@+" nuweb.redirect.exec mdlio.app.thumb.get + router:get "/action/mdlio:asset/@" mdlio.app.asset + nux.exec.optional app.routes; + :get "/" app.main; +} + + +function :asset name { + local file="$NUX_ENV_DIR/assets/nuweb/$name"; + if [ -e "$file" ]; then + mime=$(nux.mime "$file"); + echo Content-Type: $mime + echo + cat "$file" + fi +} + +function :photoswipe.html { + echo "
" +} + +} diff --git a/inc/nuweb/mdlio.app.syntax.nuxsh.sh b/inc/nuweb/mdlio.app.syntax.nuxsh.sh new file mode 100755 index 0000000..215add9 --- /dev/null +++ b/inc/nuweb/mdlio.app.syntax.nuxsh.sh @@ -0,0 +1,50 @@ +@syntax nuweb/html + +@prefix statement nux.nuxsh.statement +@prefix block nux.nuxsh.block +@prefix hs nuweb.html.syntax + +nux.use nuweb/html.syntax + +@namespace nuweb.mdlio.syntax { + function :element name tag { + block:rewrite.call nuweb.mdlio.tag.$name "nuweb.html.element $tag $@" "nuweb.html.element.end $tag" + statement:rewrite.call nuweb.mdlio.tag.$name "nuweb.html.element --close $tag $@" + } + + function :block-as-function fqn { + block:rewrite.call $fqn "function $fqn {" "}" + } + + function :statement-as-echo-function fqn { + statement:rewrite.call $fqn "function $fqn() { echo " ";}" + } + + :element card div .mdl-card.mdl-shadow--2dp + :element card-media div .mdl-card_media + :element card-title div .mdl-card_title + + :element nav nav .mdl-navigation + + + :block-as-function app.name + :block-as-function app.main + :block-as-function app.content + :block-as-function app.drawer + :block-as-function app.page-title + :block-as-function app.title + :block-as-function app.scripts + :block-as-function app.template-main + :block-as-function app.routes + + #:statement-as-echo-function app.page-title + #:statement-as-echo-function app.content +} + +function .block.app.start.plan { + identifier=app .match.namespace_block_start.plan +} + +function .block.app.end.plan { + .block.rule.namespace.end.plan +} diff --git a/inc/nuweb/mdlio.nuxsh.sh b/inc/nuweb/mdlio.nuxsh.sh new file mode 100755 index 0000000..f9c56b6 --- /dev/null +++ b/inc/nuweb/mdlio.nuxsh.sh @@ -0,0 +1,49 @@ +@syntax nuweb/html +@prefix h nuweb.html +@prefix hd nuweb.html.dynamic +nux.use nuweb/html + +function mdlio.css() { + e.link stylesheet "https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" + e.link stylesheet "https://fonts.googleapis.com/icon?family=Material+Icons" + e.link stylesheet "https://code.getmdl.io/1.3.0/material.${1}-${2}.min.css" + +} + +function e.mdlio.header++() { + +e header .mdl-layout__header .mdl-color--primary + +e div .mdl-layout__header-row + e span .mdl-layout-title $1 + e div .mdl-layout-spacer +} + +@namespace nuweb.mdlio.tag { + + function :textfield id type label { + h:div .mdl-textfield.mdl-js-textfield.mdl-textfield--floating-label { + hd:input .mdl-textfield__input @type "$type" @id "$id" "$@" + h:label .mdl-textfield__label @for $id { + echo $label + } + } + } + + function :submit name { + hd:input @type submit .mdl-button.mdl-js-button.mdl-button--raised.mdl-button--accent @value "$name" "$@" + } + + function :nav-link href { + h:a .mdl-navigation__link @href "$href" { + echo "$@" + } + } + +} + +e.alias mdlio.layout "div" ".mdl-layout .mdl-js-layout" +e.alias mdlio.main "main" ".mdl-layout__content" +e.alias mdlio.card "div" ".mdl-card.mdl-shadow--2dp" + +function .mdl_cell() { + echo mdl-cell mdl-cell--"$1"-col mdl-cell--"$2"-col-tablet mdl-cell--"$2"-col-phone +} diff --git a/inc/nuweb/router.inc.sh b/inc/nuweb/router.inc.sh old mode 100644 new mode 100755 index 133614e..c2b4396 --- a/inc/nuweb/router.inc.sh +++ b/inc/nuweb/router.inc.sh @@ -20,13 +20,18 @@ ## ## nuweb.router.tryexec.concrete() { - full_spec=$1; + method="$1"; + full_spec="$2"; + func="$3"; + shift; shift; shift; + if [ "$REQUEST_METHOD" != "$method" ]; then + return 1; + fi + path_spec=${full_spec%%\?*} query_spec=${full_spec#$path_spec} query_spec=${query_spec#\?} - func=$2; - shift; shift; nux.log trace "Checking Path Spec: '$path_spec', Query Spec: '$query_spec' Function:$func Additional Args:$@" >&2 IFS='/' read -ra spec_components <<< "$path_spec" @@ -41,12 +46,12 @@ nuweb.router.tryexec.concrete() { elif [ "$spec" == "@" ]; then path_args="$path_args $path"; elif [ "$spec" != "$path" ] ; then - return -1 + return 1 fi let i=$i+1 done if [ $i -lt "${#spec_components[@]}" ] ; then - return -1; + return 1; fi if [ -n "$query_spec" ]; then IFS='&' read -ra query_components <<< "$query_spec" @@ -59,27 +64,29 @@ nuweb.router.tryexec.concrete() { else if [ -z "$value" ]; then #echo "$def $value is empty." >&2; - return -1; + return 1; elif [ "$valueDef" == "@" ]; then query_args="$query_args $value" elif [ "$value" != "$valueDef" ]; then #echo "$def $value != $valueDef" >&2; - return -1; + return 1; fi fi done fi path_c=$(dirty.url.decode "$path_c") - $func "$@" $path_args "$path_c" $query_args + nuweb.http.query.with_env nuweb.http.post.with_env $func "$@" $path_args "$path_c" $query_args exit 0 } - -nuweb.get() { - nuweb.router.tryexec.concrete $@; +nuweb.router.get() { + nuweb.router.tryexec.concrete GET "$@"; } +nuweb.router.post() { + nuweb.router.tryexec.concrete POST "$@"; +} ## ## nuweb.router.exec:: [path] @@ -99,6 +106,7 @@ nuweb.router.exec() { path="/" fi + nux.log debug "Method: '$REQUEST_METHOD' Path: '$path'" IFS='/' read -ra PATH_COMPONENTS <<< "$path" $definition diff --git a/inc/nuweb/utils.inc.sh b/inc/nuweb/utils.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux.cfg.inc.sh b/inc/nux.cfg.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux.dsl.inc.sh b/inc/nux.dsl.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux.inc.sh b/inc/nux.inc.sh old mode 100644 new mode 100755 index 4a0a9b7..d00db17 --- a/inc/nux.inc.sh +++ b/inc/nux.inc.sh @@ -81,7 +81,6 @@ function nux.exec.or { if nux.check.function "$maybe" ; then to_exec=$maybe fi - nux.log trace "Executing $to_exec , optional was $maybe" $to_exec "$@"; } @@ -100,6 +99,8 @@ function nux.url.parse { } +nux.use nux/nuxsh + nux.use nux/log nux.use nux/check -nux.use nux/nuxsh +nux.use nux/array diff --git a/inc/nux.mime.inc.sh b/inc/nux.mime.inc.sh deleted file mode 100644 index 0e3370a..0000000 --- a/inc/nux.mime.inc.sh +++ /dev/null @@ -1,22 +0,0 @@ -nux.mime() { - local type=binary/octet - local suffix="${1##*.}" - case "${suffix,,}" in - txt) type=text/plain;; - css) type=text/css;; - jpg) type=image/jpeg;; - png) type=image/png;; - zip) type=application/zip;; - cbr) type=application/x-cbz;; - cbz) type=application/x-cbr;; - pdf) type=application/pdf;; - epub) type=application/epub+zip;; - mp4) type=video/mp4;; - *) - if [ -d "$1" ]; then - type=directory - fi - esac - echo $type; - -} diff --git a/inc/nux.thumb.inc.sh b/inc/nux.thumb.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux/array.nuxsh.sh b/inc/nux/array.nuxsh.sh new file mode 100755 index 0000000..aa0ab85 --- /dev/null +++ b/inc/nux/array.nuxsh.sh @@ -0,0 +1,13 @@ +@namespace nux.array { + + function :contains array value { + local array_ref="$array[@]"; + for c in "${!array_ref}"; do + if [ "$c" == "$value" ]; then + return 0; + fi + done + return 1 + } + +} diff --git a/inc/nux/check.inc.sh b/inc/nux/check.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux/color.nuxsh.sh b/inc/nux/color.nuxsh.sh old mode 100644 new mode 100755 diff --git a/inc/nux/dsl.inc.sh b/inc/nux/dsl.inc.sh old mode 100644 new mode 100755 index 1420ea6..494470e --- a/inc/nux/dsl.inc.sh +++ b/inc/nux/dsl.inc.sh @@ -98,11 +98,11 @@ nux.dsl.plan() { local file="$2"; local cached="${3:-$file${NUDSL_CACHE_SUFFIX}}"; if [ "$file" -ot "$cached" ]; then - nux.log debug No need to recompile. + nux.log trace No need to recompile '$file'. return; fi - nux.log debug Needs regeneration, creating new version. + nux.log trace File '$file' Needs regeneration, creating new version. local dirname=$(dirname "$cached") mkdir -p "$dirname"; @@ -110,9 +110,9 @@ nux.dsl.plan() { if (nux.dsl.process plan "$language" "$file" > "$execution_plan") ; then mv -f "$execution_plan" "$cached"; else - echo "Plan could not be generated. See errors." + echo "Plan for $file could not be generated. See errors." >&2 rm "$execution_plan" - return -1; + return 1; fi } @@ -136,7 +136,7 @@ nux.dsl.process0() { fi done if [ -n "$process_failed" ]; then - return -1; + return 1; fi done; } diff --git a/inc/nux/help.nuxsh.sh b/inc/nux/help.nuxsh.sh old mode 100644 new mode 100755 diff --git a/inc/nux.json.inc.sh b/inc/nux/json.inc.sh old mode 100644 new mode 100755 similarity index 100% rename from inc/nux.json.inc.sh rename to inc/nux/json.inc.sh diff --git a/inc/nux/log.inc.sh b/inc/nux/log.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nux/meta.nuxsh.sh b/inc/nux/meta.nuxsh.sh new file mode 100755 index 0000000..2a0dbbd --- /dev/null +++ b/inc/nux/meta.nuxsh.sh @@ -0,0 +1,50 @@ +nux_meta_backends="func file" + +@namespace nux.meta { + + function :add-impl impl { + nux_meta_backends="$impl $nux_meta_backends" + } + + function :get filename name default { + for backend in $nux_meta_backends; do + #nux.log debug "Invoking $backend nux.meta.impl.$backend.get $(declare -f nux.meta.impl.$backend.get)"; + local value=$(nux.meta.impl.$backend.get "$filename" "$name"); + if [ -n "$value" ] { + echo "$value" + return 0; + } + done + if [ -z "$default" ] { + return 1; + } + echo "$default" + } +} + +@namespace nux.meta.impl.file { + + function :get filename name { + if [ -e "$filename/.nux.meta" ] { + grep "^$name " "$filename/.nux.meta" | cut -d " " -f2- + } + } + + function :add filename name value { + local meta="$filename/.nux.meta"; + local metaline="$name $value"; + + if ! ( [ -e "$meta" ] && grep "^$metaline\$" "$meta" > /dev/null ) { + nux.log debug "Writing pin to $meta" + echo "$metaline" >> "$meta" + } + } + +} + +@namespace nux.meta.impl.func { + function :get filename name { + local funcspec="${name//:/.}" + nux.exec.optional "nux.meta.impl.func.$funcspec" "$filename" + } +} diff --git a/inc/nux/meta/impl/.gitkeep b/inc/nux/meta/impl/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/inc/nux/mime.nuxsh.sh b/inc/nux/mime.nuxsh.sh new file mode 100755 index 0000000..8684511 --- /dev/null +++ b/inc/nux/mime.nuxsh.sh @@ -0,0 +1,37 @@ +function nux.mime { + nux.mime.naive.suffix "$@" +} + +@namespace nux.mime { + + function :naive.suffix filename { + local type=binary/octet + local suffix="${filename##*.}" + suffix="${suffix,,}" + if [ -d "$filename" ]; then + type=directory + else + case "${suffix,,}" in + + txt) type=text/plain;; + css) type=text/css;; + + jpeg) ;& + jpg) type=image/jpeg;; + + png) type=image/png;; + zip) type=application/zip;; + + cbr) type=application/x-cbz;; + cbz) type=application/x-cbr;; + + pdf) type=application/pdf;; + epub) type=application/epub+zip;; + mp4) type=video/mp4;; + esac + fi + echo $type; + + } + +} diff --git a/inc/nux/mime/impl/.gitkeep b/inc/nux/mime/impl/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/inc/nux/nuxsh.inc.sh b/inc/nux/nuxsh.inc.sh old mode 100644 new mode 100755 index bc86807..0dc262a --- a/inc/nux/nuxsh.inc.sh +++ b/inc/nux/nuxsh.inc.sh @@ -1,16 +1,17 @@ nux.use nux/dsl nux.nuxsh.language.def() { - local identifier='[^ ;{}=()$]+' + local identifier_char='[^ ;{}=()$:]' + local identifier='[^ ;{}=()$:]+' local comment='(( *)(#.*))?' local whitespace="([ ]*)" - local uarg="([^ #\{\}\"'\"'\"';]+)"; + local uarg="([^ #{}\"'\"'\"';]+)"; local sarg="('\"'\"'[^'\"'\"']+'\"'\"')"; local darg='("[^"]*")'; local args="((($uarg|$darg|$sarg) *)*)"; - local prefixed_id="([^ :]*:)?($identifier)" + local prefixed_id="($identifier_char*:)?($identifier)" .match.line() { local type="$1"; @@ -34,11 +35,11 @@ nux.nuxsh.language.def() { .match.line if_start "(if)( +)$prefixed_id( +)$args?( *)(\{)" \ keyword indent2 prefix identifier indent3 args - - - - - indent4 syntax3 - .match.line function_start "((function)( +))($identifier)((\()|( *))(($identifier,? *)*)(\))?( *)(\{)" \ + .match.line function_start "((function)( +))(:?$identifier)((\()|( *))(($identifier,? *)*)(\))?( *)(\{)" \ - keyword indent2 identifier - syntax indent3 args - syntax2 indent4 syntax3 - .match.line block_start "($identifier)(( +)$args)?( *)(\{)" \ - identifier - indent2 args - - - - - indent3 syntax3 + .match.line block_start "$prefixed_id(( +)$args)?( *)(\{)" \ + prefix identifier - indent2 args - - - - - indent3 syntax3 .match.line statement "$prefixed_id(( +)$args)?( *)(;?)"\ prefix identifier - indent2 args - - - - - indent3 syntax2 @@ -69,16 +70,26 @@ nux.nuxsh.language.def() { echo ${_block_type[${#_block_type[@]}-1]} } + function .block.args.get { + echo ${_block_args[${#_block_args[@]}-1]} + } + function .block.pop { unset _block_type[${#_block_type[@]}-1] + unset _block_args[${#_block_args[@]}-1] } function .block.push { - _block_type[${#_block_type[@]}]="$1" + local block="$1";shift; + _block_type[${#_block_type[@]}]="$block" + _block_args[${#_block_args[@]}]="$@" } .match.block_start.plan() { - .block.push $identifier; + nux.log debug "P '$identifier' '$prefix' " + local identifier=$(.identifier) + nux.log debug "Block '$identifier' '$prefix' " + .block.push "$identifier" "$args"; nux.exec.or .block.$identifier.start.plan .block.start.plan } @@ -86,7 +97,7 @@ nux.nuxsh.language.def() { local identifier=$(.block.get) if [ "$identifier" == "$blocktrac_root" ]; then nux.dsl.process.fail "unnecessary block end '$line' " - return -1; + return 1; fi nux.exec.or .block.$identifier.end.plan .block.end.plan .block.pop; @@ -99,8 +110,8 @@ nux.nuxsh.language.def() { } .action.prefix() { - echo "# prefix: $1 $2" - eval "_import_prefix_$1='$2'"; + echo "# prefix: $1 ${2%.}" + eval "_import_prefix_$1='${2%.}'"; } @@ -109,20 +120,27 @@ nux.nuxsh.language.def() { local var=_import_prefix_${prefix%:} local prepend=${!var}; if [ -z "$prepend" ] ; then - nudsl.process.fail "undefined prefix: $prefix"; + nux.dsl.process.fail "undefined prefix: $prefix"; + return; fi - echo "$prepend$identifier" + echo "$prepend.$identifier" else - echo "$identifier" + cat <<< "$identifier" fi } .match.statement.plan() { - echo "${indent}$(.identifier) ${args}" + identifier=$(.identifier); + nux.exec.or .statement.$identifier.plan .statement.plan + } + .statement.plan() { + echo "${indent}$identifier ${args}" + } .match.rule.plan() { + echo "rule " >&2; eval ".action.${rule//:/.} $args"; } @@ -156,8 +174,8 @@ nux.nuxsh.language.def() { .match.function_start.plan() { .block.push function case $identifier in - .*) ;; - :*) identifier="$_namespace${identifier#:}" + :) identifier="$_namespace";; + :*) identifier="$_namespace.${identifier#:}";; esac; echo "${indent}$identifier() {"; for arg in ${args//,/ }; do @@ -169,7 +187,7 @@ nux.nuxsh.language.def() { case $identifier in function) echo "$line";; *"()") echo "$line";; - *) nudsl.process.fail Invalid block syntax: "'$identifier' '$line'"; + *) nux.dsl.process.fail Invalid block syntax: "'$identifier' '$line'"; esac; } @@ -189,6 +207,10 @@ nux.nuxsh.language.def() { """ } + .action.syntax() { + nux.use "$1.syntax" + } + .action.block.rewrite.call() { echo "# block:rewrite:block:call $@" eval """.block.$1.start.plan() { @@ -200,8 +222,56 @@ nux.nuxsh.language.def() { } """ } + + .action.block.rewrite.call2() { + echo "# block:rewrite:block:call2 $@" + nux.nuxsh.block.rewrite.call "$1" "$2" "$3" + } + } +nux.nuxsh.block.rewrite.call() { + eval """.block.$1.start.plan() { + echo \"\${indent}\"'${2}'\" \$args\" + } + + .block.$1.end.plan() { + local args=\"\$(.block.args.get)\"; + echo \"\${indent}\"'${3}' \"\$args\" + } + """ +} + +nux.nuxsh.block.rewrite.func() { + eval """ + .block.$1.start.plan() { + ${2} \"\${indent}\" \"\$args\" + } + .block.$1.end.plan() { + ${3} \"\${indent}\" \"\$args\" + } + """ +} + +nux.nuxsh.statement.rewrite.func() { + eval """ + .statement.$1.plan() { + ${2} \"\${indent}\" \"\$args\" + } + """ +} + +nux.nuxsh.statement.rewrite.call() { + eval """.statement.$1.plan() { + nux.nuxsh.statement.rewrite.call0 '$2' '$3' + } + """ +} +nux.nuxsh.statement.rewrite.call0() { + echo "${indent}${1} $args ${2}" +} + + function nux.nuxsh.use { local file="$1"; local cached="$2"; diff --git a/inc/nux.repl.inc.sh b/inc/nux/repl.inc.sh old mode 100644 new mode 100755 similarity index 100% rename from inc/nux.repl.inc.sh rename to inc/nux/repl.inc.sh diff --git a/inc/nuxfs.inc.sh b/inc/nuxfs.inc.sh old mode 100644 new mode 100755 diff --git a/inc/nuxr.nuxsh.sh b/inc/nuxr.nuxsh.sh old mode 100644 new mode 100755 index 8436199..1e66c4f --- a/inc/nuxr.nuxsh.sh +++ b/inc/nuxr.nuxsh.sh @@ -13,7 +13,7 @@ nux.use nuxr/repl else echo "$NUX_SCRIPTNAME: Unrecognized task ''$TASK' not available." echo "Try '$NUX_SCRIPTNAME help' for more information." - return -1 + return 1 } } @@ -94,7 +94,7 @@ nux.use nuxr/repl | nux.help.shelldoc return 0 else - return -1 + return 1 } } } diff --git a/inc/nuxr/repl.nuxsh.sh b/inc/nuxr/repl.nuxsh.sh old mode 100644 new mode 100755 index dfd3f1d..2ef9f19 --- a/inc/nuxr/repl.nuxsh.sh +++ b/inc/nuxr/repl.nuxsh.sh @@ -2,7 +2,7 @@ ### # # -@namespace nuxr.repl. { +@namespace nuxr.repl { function :process { backendFunc=task.$command; if nux.check.function repl.command.$command; then @@ -27,7 +27,7 @@ } -@namespace repl.command. { +@namespace repl.command { ## ## repl.command.:: ## fallback command which does nothing if user just presses enter. @@ -54,7 +54,7 @@ } -@namespace nuxr.repl.completer. { +@namespace nuxr.repl.completer { function :search.tasks { set | grep -G "^task\.$1.* ()" \ diff --git a/inc/taskie/backend.dir.inc.sh b/inc/taskie/backend.dir.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/backend.github.inc.sh b/inc/taskie/backend.github.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/backend.gogs.inc.sh b/inc/taskie/backend.gogs.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/backend.utils.inc.sh b/inc/taskie/backend.utils.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/backend.yaml.inc.sh b/inc/taskie/backend.yaml.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/common.inc.sh b/inc/taskie/common.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/githublike.inc.sh b/inc/taskie/githublike.inc.sh old mode 100644 new mode 100755 diff --git a/inc/taskie/nux.preprocess.inc.sh b/inc/taskie/nux.preprocess.inc.sh new file mode 100755 index 0000000..d52287c --- /dev/null +++ b/inc/taskie/nux.preprocess.inc.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +nux.prep.rewrite() { + +} + +nux.prep.include() { + +} + +nux.prep.exec() { + +} diff --git a/inc/thumby.inc.sh b/inc/thumby.inc.sh deleted file mode 100644 index 86ceb04..0000000 --- a/inc/thumby.inc.sh +++ /dev/null @@ -1,157 +0,0 @@ -nux.use nux.mime - -function thumby.name.shared() { - echo $(basename "$1"|md5sum|cut -d " " -f1).png -} - -function thumby.noop() { - : -} - -function thumby.mimetype.supported() { - local mimetype="$1"; - local base="${mimetype%%/*}"; - local helper="${mimetype/\//.}"; - if [ "$base" == "image" ]; then - return 0; - elif nux.check.function thumby.thumb.source.locator.$helper; then - return 0; - elif nux.check.function thumby.thumb.source.extractor.$helper; then - return 0; - fi - return 1; -} - - -function thumby.thumb.can.generate() { - local path="$1"; - local mimetype="$2"; - local helper="${mimetype/\//.}"; - - if ! thumby.mimetype.supported "$mimetype" ; then - return 1; - fi - - if nux.check.function thumby.thumb.source.locator.$helper; then - local source=$(thumby.thumb.source.locator.$helper "$path"); - if [ -z "$source" ] ; then - return 1; - fi - fi - return 0; -} - -function thumby.thumb.get() { - local path="$1"; - local mimetype="$2"; - - if [ ! -e "$path" ] ; then - return -1; - fi - - if [ -z "$mimetype" ] ; then - mimetype=$(nux.mime "$path") - fi - - local filename=$(basename "$1") - local dirname=$(dirname "$1") - local thumbname="$(thumby.name.shared "$filename")"; - local thumbpath="$dirname/.sh_thumbnails/large/$thumbname"; - if [ ! -e "$thumbpath" ]; then - - helper="${mimetype/\//.}" - nux.log debug "File $path, type $mimetype does not have thumbnail. Trying to generate using $helper." - - local preexec=thumby.noop; - local postexec=thumby.noop; - local source=$path; - local streamer=thumby.noop; - if nux.check.function thumby.thumb.source.locator.$helper ; then - source=$(thumby.thumb.source.locator.$helper "$path"); - fi - if nux.check.function thumby.thumb.source.extractor.$helper ; then - echo "Using source helper" >&2 - source="-" - streamer=thumby.thumb.source.extractor.$helper; - fi - - - mkdir -p "$dirname/.sh_thumbnails/large" &>/dev/null - mtime=`stat -c '%Y' "$path"` - - $preexec "$path" - nux.log info "Source is : $source, Streamer is $streamer"; - $streamer "$path" | convert -thumbnail '256x256>' -strip "$source" "$thumbpath" >&2 - $postexec "$path" - echo $thumbpath; - fi - -} - -function thumby.thumb.generate() { - convert -thumbnail '256x256>' -strip "$path" "$thumbpath" >&2 -} - -function thumby.get.thumb() { - local path="$1"; - local d="$(dirname "$1")"; - local f=$(basename "$1"); - local thumb_name="$(thumby.name.shared "$f")" - if [ ! -e "$path" ] ; then - return; - elif [ ! -e "$d/.sh_thumbnails/large/$thumb_name" ] ; then - #mkdir -p .sh_thumbnails/normal &>/dev/null - mkdir -p "$d/.sh_thumbnails/large" &>/dev/null - #md5=`echo $path|md5sum|cut -d" " -f1` - mtime=`stat -c '%Y' "$path"` - nux.log info "Generating thumbnails for $path $thumb_name" >&2 - #convert -thumbnail '128x128>' -strip -set Thumb::MTime "$mtime" -set Thumb::URI "$path" "$path" .sh_thumbnails/normal/$md5.$THUMB_TYPE >&2 - nux.log info "Command line is: " # echo convert -thumbnail '256x256>' -strip -set Thumb::MTime "$mtime" -set Thumb::URI "$path" "$path" .sh_thumbnails/large/$thumb_name - nux.log info convert -thumbnail '256x256>' --strip "$path" "$d"/.sh_thumbnails/large/$thumb_name - convert -thumbnail '256x256>' -strip "$path" "$d"/.sh_thumbnails/large/$thumb_name >&2 - fi - echo "$d/.sh_thumbnails/large/$thumb_name" -} - - -thumby.thumb.source.locator.directory() { - nux.log info "Using find to find jpg or png" - find "$1" -maxdepth 1 -iname "*.jpg" -or -iname "*.png" | sort -n | head -n1 -} - -thumby.thumb.source.locator.application.pdf() { - echo "$1[0]" -} - -thumby.thumb.source.extractor.application.epub+zip() { - - local rootDesc=$(unzip -p "$1" META-INF/container.xml \ - | xmlstarlet sel -N od="urn:oasis:names:tc:opendocument:xmlns:container" \ - -t -v "/od:container/od:rootfiles/od:rootfile[@media-type='application/oebps-package+xml']/@full-path" -n) - nux.log info "Root description is in: $rootDesc"; - local imgDesc=$(unzip -p "$1" "$rootDesc" \ - | xmlstarlet sel -N opf="http://www.idpf.org/2007/opf" \ - -t -m "/opf:package/opf:manifest/opf:item[@id=/opf:package/opf:metadata/opf:meta[@name='cover']/@content]" \ - -v "@href" -o ":" -v "@media-type" -n) - IFS=":" read -r img media <<< "$imgDesc"; - nux.log info "Image name is $imgDesc $img"; - if [ -n "$img" ]; then - unzip -p "$1" $img - fi -} - -thumby.thumb.source.extractor.application.x-cbr() { - suffix="${1##*.}" - case "$suffix" in - zip) ;& - cbz) - potential=$(unzip -l "$1" | sed -re "s/^ *[0-9]+ +[0-9\\-]+ +[0-9:]+ +//gi" | grep -E '\.((jpg)|(png)|(jpeg))$' | sort -n | head -n 1) - nux.log debug "Potential preview is: $potential"; - if [ -n "$potential" ]; then - unzip -p "$1" "$potential" - nux.log debug "Preview extracted." - fi - ;; - *) nux.log error "$suffix is not supported." - esac -} diff --git a/inc/thumby.nuxsh.sh b/inc/thumby.nuxsh.sh new file mode 100755 index 0000000..5bb778d --- /dev/null +++ b/inc/thumby.nuxsh.sh @@ -0,0 +1,130 @@ +nux.use nux/mime + +@namespace thumby { + function :name.shared { + echo $(basename "$1"|md5sum|cut -d " " -f1).png + } + + function :noop { + : + } + + function :image.size { + raw_str=$(identify "$1") + str=${raw_str#$1 * } + echo ${str%% *} + } + + function :mimetype.supported { + local mimetype="$1"; + local base="${mimetype%%/*}"; + local helper="${mimetype/\//.}"; + if [ "$base" == "image" ]; then + return 0; + elif nux.check.function thumby.thumb.source.locator.$helper; then + return 0; + elif nux.check.function thumby.thumb.source.extractor.$helper; then + return 0; + fi + return 1; + } +} + +@namespace thumby.thumb { + + function :should.generate path mimetype { + local thumbpath=$(thumby.thumb.path "$path"); + local source=$(thumby.thumb.source "$path" "$mimetype"); + if [ "$source" -nt "$thumbpath" ]; then + return 0; + else + return 1; + fi + } + + function :can.generate path mimetype { + local helper="${mimetype/\//.}"; + + if ! thumby.mimetype.supported "$mimetype" { + return 1; + } + + if nux.check.function thumby.thumb.source.locator.$helper { + local source=$(thumby.thumb.source.locator.$helper "$path"); + if [ -z "$source" ] { + return 1; + } + } + return 0; + } + + function :source path mimetype { + local helper="${mimetype/\//.}"; + if nux.check.function thumby.thumb.source.locator.$helper; then + thumby.thumb.source.locator.$helper "$path" + else + echo "$path"; + fi + } + + function :path path { + local filename=$(basename "$path") + local dirname=$(dirname "$path") + local thumbname="$(thumby.name.shared "$filename")"; + echo "$dirname/.sh_thumbnails/large/$thumbname"; + } + + function :get path mimetype { + + nux.log debug "thumby path: $path"; + if [ ! -e "$path" ] ; then + return 1; + fi + + if [ -z "$mimetype" ] ; then + mimetype=$(nux.mime "$path") + fi + + local filename=$(basename "$path") + local dirname=$(dirname "$path") + local thumbname="$(thumby.name.shared "$filename")"; + local thumbpath="$dirname/.sh_thumbnails/large/$thumbname"; + + nux.log debug "Dir: $dirname, File: $filename, thumb: $thumbname" + + if thumby.thumb.should.generate "$path" "$mimetype" { + + helper="${mimetype/\//.}" + nux.log debug "File $path, type $mimetype does not have thumbnail. Trying to generate using $helper." + + local preexec=thumby.noop; + local postexec=thumby.noop; + local source=$path; + local streamer=thumby.noop; + if nux.check.function thumby.thumb.source.locator.$helper ; then + source=$(thumby.thumb.source.locator.$helper "$path"); + fi + if nux.check.function thumby.thumb.source.extractor.$helper ; then + echo "Using source helper" >&2 + source="-" + streamer=thumby.thumb.source.extractor.$helper; + fi + nux.log debug "File $path, using '$source' as source. Using stremer '$streamer'" + mkdir -p "$dirname/.sh_thumbnails/large" &>/dev/null + mtime=`stat -c '%Y' "$path"` + + $preexec "$path" + nux.log info "Source is : $source, Streamer is $streamer"; + $streamer "$path" | convert -thumbnail '256x256>' -strip "$source" "$thumbpath" >&2 + $postexec "$path" + } + if [ -e "$thumbpath" ]; then + echo $thumbpath; + fi + } + + function :generate { + convert -thumbnail '256x256>' -strip "$path" "$thumbpath" >&2 + } + +} diff --git a/inc/thumby/builtin.nuxsh.sh b/inc/thumby/builtin.nuxsh.sh new file mode 100755 index 0000000..cc0d16c --- /dev/null +++ b/inc/thumby/builtin.nuxsh.sh @@ -0,0 +1,50 @@ + +@namespace thumby.thumb.source { + + function :locator.directory { + if [ -e "$1/.thumb.jpg" ]; then + echo "$1/.thumb.jpg"; + elif [ -e "$1/.thumb.png" ]; then + echo "$1/.thumb.png"; + # find -L "$1" -maxdepth 1 -iname "*.jpg" -or -iname "*.png" | sort -n | head -n1 + fi + } + + function :locator.application.pdf { + echo "$1[0]" + } + + function :extractor.application.epub+zip() { + + local rootDesc=$(unzip -p "$1" META-INF/container.xml \ + | xmlstarlet sel -N od="urn:oasis:names:tc:opendocument:xmlns:container" \ + -t -v "/od:container/od:rootfiles/od:rootfile[@media-type='application/oebps-package+xml']/@full-path" -n) + nux.log info "Root description is in: $rootDesc"; + local imgDesc=$(unzip -p "$1" "$rootDesc" \ + | xmlstarlet sel -N opf="http://www.idpf.org/2007/opf" \ + -t -m "/opf:package/opf:manifest/opf:item[@id=/opf:package/opf:metadata/opf:meta[@name='cover']/@content]" \ + -v "@href" -o ":" -v "@media-type" -n) + IFS=":" read -r img media <<< "$imgDesc"; + nux.log info "Image name is $imgDesc $img"; + if [ -n "$img" ]; then + unzip -p "$1" $img + fi + } + + function :extractor.application.x-cbr() { + suffix="${1##*.}" + case "$suffix" in + zip) ;& + cbz) + potential=$(unzip -l "$1" | sed -re "s/^ *[0-9]+ +[0-9\\-]+ +[0-9:]+ +//gi" | grep -E '\.((jpg)|(png)|(jpeg))$' | sort -n | head -n 1) + nux.log debug "Potential preview is: $potential"; + if [ -n "$potential" ]; then + unzip -p "$1" "$potential" + nux.log debug "Preview extracted." + fi + ;; + *) nux.log error "$suffix is not supported." + esac + } + +}