├── .gitignore
├── LICENSE
├── README.md
├── _config.yml
├── dist
└── observable-press.js
├── examples
├── aai
│ ├── index.html
│ ├── observable-press.js
│ └── style.css
├── pantry
│ ├── index.html
│ ├── observable-press.js
│ └── style.css
├── simple-ui
│ ├── index.html
│ ├── observable-press.js
│ └── style.css
├── simple
│ ├── index.html
│ ├── observable-press.js
│ └── style.css
└── todo
│ ├── index.html
│ ├── observable-press.js
│ └── style.css
├── package-lock.json
├── package.json
├── src
├── index.js
├── initialize.js
├── library.js
├── style.css
└── util.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Zev Youra
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # observable-press
2 | An opinionated way to publish [Observable notebooks](//observablehq.com).
3 |
4 | The core idea is to do all your coding in your notebook, write an HTML skeleton for its presentation, and let `observable-press` glue it all together.
5 |
6 | The [Observable Runtime](https://github.com/observablehq/runtime) makes this pretty easy to do with no library, but `observable-press` adds a consistent pattern that allows you to write zero code outside your notebook, and also adds some small conveniences (initial loading indicator, new `height` builtin).
7 |
8 | ### Simplest Example
9 |
10 | [code](examples/simple/index.html) // [demo](https://zzzev.github.io/observable-press/examples/simple) // [source notebook](//observablehq.com/@zzzev/slit-scan-effect)
11 |
12 | A very simple example looks like this:
13 | ```
14 |
15 |
16 |
17 |
18 |
19 | ```
20 |
21 | First, we include some simple default styles. These are optional, though you will need some similar styles if you want to have full-window rendering with no margins.
22 |
23 | Then, we include the `observable-press` library in a script tag. The `data-notebook` attribute on this tag is used to specify the notebook that should be loaded.
24 |
25 | `data-override-height` is an optional attribute, if present it will delete the named variable `height` from the notebook (which causes it to instead use the builtin provided by `observable-press` which matches window height).
26 |
27 | Because there are no HTML nodes in the document with `data-cell` set, the library picks the first "content-like" variable in the notebook and renders it into the body.
28 |
29 | ### Using `data-cell`s and `.loading`
30 |
31 | [code](examples/simple-ui/index.html) // [demo](https://zzzev.github.io/observable-press/examples/simple-ui) // [source notebook](//observablehq.com/@zzzev/reversable-zoom)
32 |
33 | A slightly more complex example has an HTML body like this:
34 | ```
35 |
36 |
37 |
loading...
38 | ```
39 |
40 | This includes two tags with `data-cell` attributes, which will be populated by the matching named variables from the notebook. Since there's at least one explicitly specified `data-cell`, it won't pick one automatically. Note that `viewof` must be included in the cell name if you want to render the view of a variable.
41 |
42 | This also includes a tag with the class `loading`, which will be automatically removed once the specified `data-cell`s have all rendered at least once. This makes it easy to include a simple loading indicator.
43 |
44 | ### Other Examples
45 | #### Pantry Nutrition Explorer
46 | [code](examples/pantry/index.html) // [demo](https://zzzev.github.io/observable-press/examples/pantry) // [source notebook](//observablehq.com/@zzzev/pantry-nutrition-explorer)
47 |
48 | #### TODO List
49 | [code](examples/todo/index.html) // [demo](https://zzzev.github.io/observable-press/examples/todo) // [source notebook](//observablehq.com/@zzzev/todopress)
50 |
51 | Based on [TodoMVC](//todomvc.com).
52 |
53 | #### Animated Average Images
54 | [code](examples/aai/index.html) // [demo](https://zzzev.github.io/observable-press/examples/aai) // [source notebook](//observablehq.com/@zzzev/animated-average-images-ii)
55 |
56 | Note: the animation in this notebook is somewhat choppy.
57 |
58 | ## Troubleshooting
59 | If your notebook has special CORS access rules set up, it probably won't work if rehosted on a different domain.
60 |
61 | ## TODOs
62 | - Nicer default loading indicator
63 | - Better pending/error indication
64 |
65 | ## Licensing
66 | `observable-press` is MIT licensed
67 |
68 | Notebooks published on Observable without an explicit license are licensed under [the terms described here](https://observablehq.com/terms-of-service), and cannot be freely re-used on a different site unless you are the original author or have permission.
69 |
70 | This project is not affiliated or endorsed by Observable, Inc.
71 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/dist/observable-press.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.observablePress=t():e.observablePress=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=15)}([function(e,t,n){"use strict";n.d(t,"c",(function(){return l})),n.d(t,"b",(function(){return u})),n.d(t,"a",(function(){return c})),n.d(t,"e",(function(){return d})),n.d(t,"d",(function(){return h}));var r=n(7),i=n(5),o=n(1),s=n(6),a=n(2),l=1,u=2,c=3,d={};function h(e,t,n){var r;null==n&&(n=d),Object.defineProperties(this,{_observer:{value:n,writable:!0},_definition:{value:m,writable:!0},_duplicate:{value:void 0,writable:!0},_duplicates:{value:void 0,writable:!0},_indegree:{value:NaN,writable:!0},_inputs:{value:[],writable:!0},_invalidate:{value:a.a,writable:!0},_module:{value:t},_name:{value:null,writable:!0},_outputs:{value:new Set,writable:!0},_promise:{value:Promise.resolve(void 0),writable:!0},_reachable:{value:n!==d,writable:!0},_rejector:{value:(r=this,function(e){if(e===m)throw new o.a(r._name+" is not defined",r._name);throw new o.a(r._name+" could not be resolved",r._name)})},_type:{value:e},_value:{value:void 0,writable:!0},_version:{value:0,writable:!0}})}function f(e){e._module._runtime._dirty.add(e),e._outputs.add(this)}function p(e){e._module._runtime._dirty.add(e),e._outputs.delete(this)}function m(){throw m}function g(e){return function(){throw new o.a(e+" is defined more than once")}}function b(e,t,n){var r=this._module._scope,i=this._module._runtime;if(this._inputs.forEach(p,this),t.forEach(f,this),this._inputs=t,this._definition=n,this._value=void 0,n===a.a?i._variables.delete(this):i._variables.add(this),e==this._name&&r.get(e)===this)this._outputs.forEach(i._updates.add,i._updates);else{var o,s;if(this._name)if(this._outputs.size)r.delete(this._name),(s=this._module._resolve(this._name))._outputs=this._outputs,this._outputs=new Set,s._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(this)]=s}),this),s._outputs.forEach(i._updates.add,i._updates),i._dirty.add(s).add(this),r.set(this._name,s);else if((s=r.get(this._name))===this)r.delete(this._name);else{if(s._type!==c)throw new Error;s._duplicates.delete(this),this._duplicate=void 0,1===s._duplicates.size&&(s=s._duplicates.keys().next().value,o=r.get(this._name),s._outputs=o._outputs,o._outputs=new Set,s._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(o)]=s})),s._definition=s._duplicate,s._duplicate=void 0,i._dirty.add(o).add(s),i._updates.add(s),r.set(this._name,s))}if(this._outputs.size)throw new Error;e&&((s=r.get(e))?s._type===c?(this._definition=g(e),this._duplicate=n,s._duplicates.add(this)):s._type===u?(this._outputs=s._outputs,s._outputs=new Set,this._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(s)]=this}),this),i._dirty.add(s).add(this),r.set(e,this)):(s._duplicate=s._definition,this._duplicate=n,(o=new h(c,this._module))._name=e,o._definition=this._definition=s._definition=g(e),o._outputs=s._outputs,s._outputs=new Set,o._outputs.forEach((function(e){e._inputs[e._inputs.indexOf(s)]=o})),o._duplicates=new Set([this,s]),i._dirty.add(s).add(o),i._updates.add(s).add(o),r.set(e,o)):r.set(e,this)),this._name=e}return i._updates.add(this),i._compute(),this}Object.defineProperties(h.prototype,{_pending:{value:function(){this._observer.pending&&this._observer.pending()},writable:!0,configurable:!0},_fulfilled:{value:function(e){this._observer.fulfilled&&this._observer.fulfilled(e,this._name)},writable:!0,configurable:!0},_rejected:{value:function(e){this._observer.rejected&&this._observer.rejected(e,this._name)},writable:!0,configurable:!0},define:{value:function(e,t,n){switch(arguments.length){case 1:n=e,e=t=null;break;case 2:n=t,"string"==typeof e?t=null:(t=e,e=null)}return b.call(this,null==e?null:e+"",null==t?[]:r.b.call(t,this._module._resolve,this._module),"function"==typeof n?n:Object(i.a)(n))},writable:!0,configurable:!0},delete:{value:function(){return b.call(this,null,[],a.a)},writable:!0,configurable:!0},import:{value:function(e,t,n){arguments.length<3&&(n=t,t=e);return b.call(this,t+"",[n._resolve(e+"")],s.a)},writable:!0,configurable:!0}})},function(e,t,n){"use strict";function r(e,t){this.message=e+"",this.input=t}n.d(t,"a",(function(){return r})),r.prototype=Object.create(Error.prototype),r.prototype.name="RuntimeError",r.prototype.constructor=r},function(e,t,n){"use strict";t.a=function(){}},function(e,t,n){"use strict";(function(e){n.d(t,"b",(function(){return d})),n.d(t,"c",(function(){return h})),n.d(t,"a",(function(){return f}));var r=n(4),i=n(1),o=n(10),s=n(11),a=n(8),l=n(2),u=n(0);const c="function"==typeof requestAnimationFrame?requestAnimationFrame:e;var d={},h={};function f(e=new r.b,t=k){var n=this.module();if(Object.defineProperties(this,{_dirty:{value:new Set},_updates:{value:new Set},_computing:{value:null,writable:!0},_init:{value:null,writable:!0},_modules:{value:new Map},_variables:{value:new Set},_disposed:{value:!1,writable:!0},_builtin:{value:n},_global:{value:t}}),e)for(var i in e)new u.d(u.b,n).define(i,[],e[i])}function p(e){const t=new Set(e._inputs);for(const n of t){if(n===e)return!0;n._inputs.forEach(t.add,t)}return!1}function m(e){++e._indegree}function g(e){--e._indegree}function b(e){return e._promise.catch(e._rejector)}function v(e){return new Promise((function(t){e._invalidate=t}))}function _(e,t){let n,r,i="function"==typeof IntersectionObserver&&t._observer&&t._observer._node,o=!i,s=l.a,a=l.a;return i&&((r=new IntersectionObserver(([e])=>(o=e.isIntersecting)&&(n=null,s()))).observe(i),e.then(()=>(r.disconnect(),r=null,a()))),function(e){return o?Promise.resolve(e):r?(n||(n=new Promise((e,t)=>(s=e,a=t))),n.then(()=>e)):Promise.reject()}}function y(e){e._invalidate(),e._invalidate=l.a,e._pending();var t=e._value,n=++e._version,r=null,i=e._promise=Promise.all(e._inputs.map(b)).then((function(i){if(e._version===n){for(var o=0,s=i.length;ot._reachable?this._updates.add(t):n{e._invalidate(),e._version=NaN})},writable:!0,configurable:!0},module:{value:function(e,t=l.a){let n;if(void 0===e)return(n=this._init)?(this._init=null,n):new a.a(this);if(n=this._modules.get(e))return n;this._init=n=new a.a(this),this._modules.set(e,n);try{e(this,t)}finally{this._init=null}return n},writable:!0,configurable:!0},fileAttachments:{value:r.a,writable:!0,configurable:!0}})}).call(this,n(12).setImmediate)},function(e,t,n){"use strict";async function r(e){const t=await fetch(await e.url());if(!t.ok)throw new Error(`Unable to load file: ${e.name}`);return t}class i{constructor(e,t){Object.defineProperties(this,{_url:{value:e},name:{value:t,enumerable:!0}})}async url(){return this._url}async blob(){return(await r(this)).blob()}async arrayBuffer(){return(await r(this)).arrayBuffer()}async text(){return(await r(this)).text()}async json(){return(await r(this)).json()}async stream(){return(await r(this)).body}async image(){const e=await this.url();return new Promise((t,n)=>{const r=new Image;new URL(e,document.baseURI).origin!==new URL(location).origin&&(r.crossOrigin="anonymous"),r.onload=()=>t(r),r.onerror=()=>n(new Error(`Unable to load file: ${this.name}`)),r.src=e})}}function o(e){throw new Error(`File not found: ${e}`)}function s(e){return t=>{const n=e(t+="");if(null==n)throw new Error(`File not found: ${t}`);return new i(n,t)}}var a=function(e){return function(){return e}},l={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},u=0;function c(e){this.id=e,this.href=new URL(`#${e}`,location)+""}c.prototype.toString=function(){return"url("+this.href+")"};var d={canvas:function(e,t){var n=document.createElement("canvas");return n.width=e,n.height=t,n},context2d:function(e,t,n){null==n&&(n=devicePixelRatio);var r=document.createElement("canvas");r.width=e*n,r.height=t*n,r.style.width=e+"px";var i=r.getContext("2d");return i.scale(n,n),i},download:function(e,t="untitled",n="Save"){const r=document.createElement("a"),i=r.appendChild(document.createElement("button"));async function o(){await new Promise(requestAnimationFrame),URL.revokeObjectURL(r.href),r.removeAttribute("href"),i.textContent=n,i.disabled=!1}return i.textContent=n,r.download=t,r.onclick=async t=>{if(i.disabled=!0,r.href)return o();i.textContent="Saving…";try{const t=await("function"==typeof e?e():e);i.textContent="Download",r.href=URL.createObjectURL(t)}catch(e){i.textContent=n}if(t.eventPhase)return o();i.disabled=!1},r},element:function(e,t){var n,r=e+="",i=r.indexOf(":");i>=0&&"xmlns"!==(r=e.slice(0,i))&&(e=e.slice(i+1));var o=l.hasOwnProperty(r)?document.createElementNS(l[r],e):document.createElement(e);if(t)for(var s in t)i=(r=s).indexOf(":"),n=t[s],i>=0&&"xmlns"!==(r=s.slice(0,i))&&(s=s.slice(i+1)),l.hasOwnProperty(r)?o.setAttributeNS(l[r],s,n):o.setAttribute(s,n);return o},input:function(e){var t=document.createElement("input");return null!=e&&(t.type=e),t},range:function(e,t,n){1===arguments.length&&(t=e,e=null);var r=document.createElement("input");return r.min=e=null==e?0:+e,r.max=t=null==t?1:+t,r.step=null==n?"any":n=+n,r.type="range",r},select:function(e){var t=document.createElement("select");return Array.prototype.forEach.call(e,(function(e){var n=document.createElement("option");n.value=n.textContent=e,t.appendChild(n)})),t},svg:function(e,t){var n=document.createElementNS("http://www.w3.org/2000/svg","svg");return n.setAttribute("viewBox",[0,0,e,t]),n.setAttribute("width",e),n.setAttribute("height",t),n},text:function(e){return document.createTextNode(e)},uid:function(e){return new c("O-"+(null==e?"":e+"-")+ ++u)}},h={buffer:function(e){return new Promise((function(t,n){var r=new FileReader;r.onload=function(){t(r.result)},r.onerror=n,r.readAsArrayBuffer(e)}))},text:function(e){return new Promise((function(t,n){var r=new FileReader;r.onload=function(){t(r.result)},r.onerror=n,r.readAsText(e)}))},url:function(e){return new Promise((function(t,n){var r=new FileReader;r.onload=function(){t(r.result)},r.onerror=n,r.readAsDataURL(e)}))}};function f(){return this}function p(e,t){let n=!1;return{[Symbol.iterator]:f,next:()=>n?{done:!0}:(n=!0,{done:!1,value:e}),return:()=>(n=!0,t(e),{done:!0}),throw:()=>({done:n=!0})}}var m=function(e){let t,n,r=!1;const i=e((function(e){n?(n(e),n=null):r=!0;return t=e}));return{[Symbol.iterator]:f,throw:()=>({done:!0}),return:()=>(null!=i&&i(),{done:!0}),next:function(){return{done:!1,value:r?(r=!1,Promise.resolve(t)):new Promise(e=>n=e)}}}};function g(e){switch(e.type){case"range":case"number":return e.valueAsNumber;case"date":return e.valueAsDate;case"checkbox":return e.checked;case"file":return e.multiple?e.files:e.files[0];default:return e.value}}var b={disposable:p,filter:function*(e,t){for(var n,r=-1;!(n=e.next()).done;)t(n.value,++r)&&(yield n.value)},input:function(e){return m((function(t){var n=function(e){switch(e.type){case"button":case"submit":case"checkbox":return"click";case"file":return"change";default:return"input"}}(e),r=g(e);function i(){t(g(e))}return e.addEventListener(n,i),void 0!==r&&t(r),function(){e.removeEventListener(n,i)}}))},map:function*(e,t){for(var n,r=-1;!(n=e.next()).done;)yield t(n.value,++r)},observe:m,queue:function(e){let t;const n=[],r=e((function(e){n.push(e),t&&(t(n.shift()),t=null);return e}));return{[Symbol.iterator]:f,throw:()=>({done:!0}),return:()=>(null!=r&&r(),{done:!0}),next:function(){return{done:!1,value:n.length?Promise.resolve(n.shift()):new Promise(e=>t=e)}}}},range:function*(e,t,n){e=+e,t=+t,n=(i=arguments.length)<2?(t=e,e=0,1):i<3?1:+n;for(var r=-1,i=0|Math.max(0,Math.ceil((t-e)/n));++r{n.terminate(),URL.revokeObjectURL(t)})}};function v(e,t){return function(n){var r,i,o,s,a,l,u,c,d=n[0],h=[],f=null,p=-1;for(a=1,l=arguments.length;a0){for(o=new Array(p),s=document.createTreeWalker(f,NodeFilter.SHOW_COMMENT,null,!1);s.nextNode();)i=s.currentNode,/^o:/.test(i.nodeValue)&&(o[+i.nodeValue.slice(2)]=i);for(a=0;a