\r\n

\r\n\"\"\"\r\n\r\nsrcUrl = ->\r\n URL.createObjectURL new Blob [htmlContent()],\r\n type: \"text/html; charset=utf-8\"\r\n\r\ndataUrl = -> \"data:text/html;base64,#{btoa(htmlContent())}\"\r\n\r\ntestFrame = (fn) ->\r\n iframe = document.createElement('iframe')\r\n iframe.name = \"iframe-#{randId()}\"\r\n iframe.src = srcUrl()\r\n document.body.appendChild(iframe)\r\n\r\n postmaster = Postmaster\r\n remoteTarget: ->\r\n iframe.contentWindow\r\n\r\n iframe.addEventListener \"load\", ->\r\n fn(postmaster)\r\n .finally ->\r\n iframe.remove()\r\n postmaster.dispose()\r\n\r\n return\r\n\r\ndescribe \"Postmaster\", ->\r\n # Can't open child windows from within sandboxed iframes?\r\n it.skip \"should work with openened windows\", (done) ->\r\n childWindow = window.open(srcUrl(), \"child-#{randId()}\", \"width=200,height=200\")\r\n\r\n postmaster = Postmaster\r\n remoteTarget: -> childWindow\r\n\r\n childWindow.addEventListener \"load\", ->\r\n postmaster.send \"echo\", 5\r\n .then (result) ->\r\n assert.equal result, 5\r\n .then ->\r\n done()\r\n , (error) ->\r\n done(error)\r\n .then ->\r\n childWindow.close()\r\n postmaster.dispose()\r\n\r\n return\r\n\r\n it \"should work with iframes\", (done) ->\r\n testFrame (postmaster) ->\r\n postmaster.send \"echo\", 17\r\n .then (result) ->\r\n assert.equal result, 17\r\n .then done, done\r\n\r\n return\r\n\r\n it \"should handle the remote call throwing errors\", (done) ->\r\n testFrame (postmaster) ->\r\n postmaster.send \"throws\"\r\n .then ->\r\n done new Error \"Expected an error\"\r\n , (error) ->\r\n done()\r\n\r\n return\r\n\r\n it \"should throwing a useful error when the remote doesn't define the function\", (done) ->\r\n testFrame (postmaster) ->\r\n postmaster.send \"undefinedFn\"\r\n .then ->\r\n done new Error \"Expected an error\"\r\n , (error) ->\r\n done()\r\n\r\n return\r\n\r\n it \"should handle the remote call returning failed promises\", (done) ->\r\n testFrame (postmaster) ->\r\n postmaster.send \"promiseFail\"\r\n .then ->\r\n done new Error \"Expected an error\"\r\n , (error) ->\r\n done()\r\n\r\n return\r\n\r\n it \"should be able to go around the world\", (done) ->\r\n testFrame (postmaster) ->\r\n postmaster.yolo = (txt) ->\r\n \"heyy #{txt}\"\r\n postmaster.send \"send\", \"yolo\", \"cool\"\r\n .then (result) ->\r\n assert.equal result, \"heyy cool\"\r\n .then ->\r\n done()\r\n , (error) ->\r\n done(error)\r\n\r\n return\r\n\r\n it.skip \"should work with web workers\", (done) ->\r\n blob = new Blob [scriptContent()], type: \"application/javascript\"\r\n jsUrl = URL.createObjectURL(blob)\r\n\r\n worker = new Worker(jsUrl)\r\n\r\n postmaster = Postmaster\r\n remoteTarget: -> worker\r\n receiver: -> worker\r\n\r\n setTimeout ->\r\n postmaster.send \"echo\", 17\r\n .then (result) ->\r\n assert.equal result, 17\r\n .then ->\r\n done()\r\n , (error) ->\r\n done(error)\r\n .finally ->\r\n worker.terminate()\r\n , 100\r\n\r\n return\r\n\r\n it \"should fail quickly when contacting a window that doesn't support Postmaster\", (done) ->\r\n iframe = document.createElement('iframe')\r\n document.body.appendChild(iframe)\r\n\r\n childWindow = iframe.contentWindow\r\n postmaster = Postmaster\r\n remoteTarget: -> childWindow\r\n ackTimeout: -> 30\r\n\r\n postmaster.send \"echo\", 5\r\n .catch (e) ->\r\n if e.message.match /no ack/i\r\n done()\r\n else\r\n done(1)\r\n .finally ->\r\n iframe.remove()\r\n postmaster.dispose()\r\n\r\n return\r\n\r\n it \"should return a rejected promise when unable to send to the target\", (done) ->\r\n postmaster = Postmaster\r\n remoteTarget: -> null\r\n \r\n postmaster.send \"yo\"\r\n .then ->\r\n done throw new Error \"Expected an error\"\r\n , (e) ->\r\n assert.equal e.message, \"No remote target\"\r\n done()\r\n .catch done\r\n .finally ->\r\n postmaster.dispose()\r\n\r\n return\r\n\r\n it \"should log\", ->\r\n called = false\r\n\r\n postmaster = Postmaster\r\n logger:\r\n info: ->\r\n called = true\r\n\r\n assert called\r\n postmaster.dispose()\r\n" }, "lib/test/ui.coffee": { "content": "{AceEditor} = ui = require \"../ui/index\"\n\ndescribe \"ui\", ->\n it \"should provide an ace editor view\", ->\n {initSession, modeFor} = AceEditor\n assert initSession\n assert modeFor\n\n assert.equal modeFor(\"file.js\"), \"javascript\"\n" }, "templates/reader-input.coffee": { "content": "module.exports = (options={}) ->\n input = document.createElement('input')\n input.type = \"file\"\n input.setAttribute \"accept\", options.accept\n\n input.onchange = (e) ->\n options.select? input.files[0]\n\n return input\n" }, "lib/mousetrap.js": { "content": "/* mousetrap v1.6.3 craig.is/killing/mice */\r\n(function(q,u,c){function v(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent(\"on\"+b,g)}function z(a){if(\"keypress\"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return n[a.which]?n[a.which]:r[a.which]?r[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push(\"shift\");a.altKey&&b.push(\"alt\");a.ctrlKey&&b.push(\"ctrl\");a.metaKey&&b.push(\"meta\");return b}function w(a){return\"shift\"==a||\"ctrl\"==a||\"alt\"==a||\r\n\"meta\"==a}function A(a,b){var g,d=[];var e=a;\"+\"===e?e=[\"+\"]:(e=e.replace(/\\+{2}/g,\"+plus\"),e=e.split(\"+\"));for(g=0;gc||n.hasOwnProperty(c)&&(p[n[c]]=c)}g=p[e]?\"keydown\":\"keypress\"}\"keypress\"==g&&d.length&&(g=\"keydown\");return{key:m,modifiers:d,action:g}}function D(a,b){return null===a||a===u?!1:a===b?!0:D(a.parentNode,b)}function d(a){function b(a){a=\r\na||{};var b=!1,l;for(l in p)a[l]?b=!0:p[l]=0;b||(x=!1)}function g(a,b,t,f,g,d){var l,E=[],h=t.type;if(!k._callbacks[a])return[];\"keyup\"==h&&w(a)&&(b=[a]);for(l=0;l\":\".\",\"?\":\"/\",\"|\":\"\\\\\"},B={option:\"alt\",command:\"meta\",\"return\":\"enter\",\r\nescape:\"esc\",plus:\"+\",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?\"meta\":\"ctrl\"},p;for(c=1;20>c;++c)n[111+c]=\"f\"+c;for(c=0;9>=c;++c)n[c+96]=c.toString();d.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};d.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};d.prototype.trigger=function(a,b){if(this._directMap[a+\":\"+b])this._directMap[a+\":\"+b]({},a);return this};d.prototype.reset=function(){this._callbacks={};\r\nthis._directMap={};return this};d.prototype.stopCallback=function(a,b){if(-1<(\" \"+b.className+\" \").indexOf(\" mousetrap \")||D(b,this.target))return!1;if(\"composedPath\"in a&&\"function\"===typeof a.composedPath){var c=a.composedPath()[0];c!==a.target&&(b=c)}return\"INPUT\"==b.tagName||\"SELECT\"==b.tagName||\"TEXTAREA\"==b.tagName||b.isContentEditable};d.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};d.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(n[b]=a[b]);p=null};\r\nd.init=function(){var a=d(u),b;for(b in a)\"_\"!==b.charAt(0)&&(d[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};d.init();\"undefined\"!==typeof module&&module.exports&&(module.exports=d);\"function\"===typeof define&&define.amd&&define(function(){return d})}})(\"undefined\"!==typeof window?window:null,\"undefined\"!==typeof window?document:null);\r\n" }, "lib/test/mousetrap.js": { "content": "" }, "docs/app.md": { "content": "App\n===\n\nThe system runtime provides an app base with many useful methods:\n\n- `confirmUnsaved`\n- `currentPath` Observable string\n- `drop` handler that receives an array of Files on a drop event.\n- `exit`\n- `extend`\n- `hotkey`\n- `new`\n- `open`\n- `save`\n- `saved` Observable bool representing the state of the app, saved or not.\n- `saveAs`\n- `paste` handler that receives an array of Files on a paste event.\n\nYour app must provide these to make use of the paste/drop/fileIO interfaces.\n\n- `loadFile`\n- `newFile`\n- `saveData`\n\nOne can optionally provide a `menu` property:\n\n```\nmenu: \"\"\"\n File\n New\n Open\n Save\n Save As\n ---\n Exit\n Edit\n Undo\n Redo\n Resize -> doResize\n Example\n Item One\n Item Two\n Submenu\n Sub Item One\n Sub Item Two\n\"\"\"\n```\n\nThis menu micro-format defines a menubar for your app. The menu items have the\nname given delegating to the method of the same name except non-alphanumeric\ncharacters are removed and the initial character is downcased, i.e. `Save As` ->\n`saveAs`.\n\nOther app methods that you can optionally define:\n\n- `title`\n\nTitle will be observed and piped to the \"host application\" if it is a function with\nobservable dependencies.\n\nThe main idea is to combine all the common behaviors into one comprehensive\nfocal point.\n\nGlossary\n--------\n\n### Observables\n\nObservable properties are functions that store a value when called\nwith one argument and return that value when called with zero arguments. They\nhave an `observe` method.\n\nObservable functions will automatically re-execute if they depend on observable\nproperties. They may be composed of any number of observable properties or\nfunctions.\n\n### Host\n\nCommunication channel to the host environment.\n\n```\nhost.writeFile(path, blob)\n```\n\nThe host environment is ZineOS or none if standalone. There may be other possible\nhost environments, it could be the host OS if running in Electron.\n\n### Host Application\n\nThe ZineOS application object running inside the ZineOS frame. This holds info\nabout saved status, the title, a handle to the iframe or app element, and the\nactual window element in ZineOS.\n\nIn standalone the host application is a thin wrapper over the browser chrome.\n\nIn summary the host application is the host environment's representation of this\napplication.\n" }, "lib/runtime.coffee": { "content": "# runtime is what prepares the environment for user apps\n# we hook up the postmaster and proxy messages to the OS\n\n{version} = require \"../pixie\"\n\nPostmaster = require \"./postmaster\"\n{applyStyle, Observable, Style} = require \"../lib/ui/index\"\n\nRuntime = (system, opts={}) ->\n if opts.applyStyle\n applyStyle(Style.all, 'system')\n\n opts.logger ?=\n info: ->\n debug: ->\n\n externalObservables = {}\n\n # Queue up messages until a delegate is assigned\n heldApplicationMessages = []\n\n postmaster = Postmaster\n logger: opts.logger\n # For receiving messages from the system\n delegate:\n application: (method, args...) ->\n if applicationTarget.delegate\n applicationTarget.delegate[method](args...)\n else\n # This promise should keep the channel unresolved until the future\n new Promise (resolve, reject) ->\n heldApplicationMessages.push (delegate) ->\n try\n resolve delegate[method](args...)\n catch e\n reject e\n \n updateSignal: (name, newValue) ->\n externalObservables[name](newValue)\n \n fn: (handlerId, args) ->\n # TODO: `this` is null but should be `system` here for bound events.\n eventListeners[handlerId].apply(null, args)\n\n remoteExists = postmaster.remoteTarget()\n\n applicationTarget =\n observeSignal: (name, handler) ->\n observable = Observable()\n externalObservables[name] = observable\n\n observable.observe handler\n\n # Invoke the handler with the initial value\n postmaster.send \"application\", \"observeSignal\", name\n .then handler\n\n # For sending messages to ZineOS application side\n applicationProxy = new Proxy applicationTarget,\n get: (target, property, receiver) ->\n target[property] or\n ->\n return unless remoteExists\n postmaster.send \"application\", property, arguments...\n set: (target, property, value, receiver) ->\n if property is \"delegate\"\n heldApplicationMessages.forEach (fn)->\n fn(value)\n\n heldApplicationMessages = []\n\n target[property] = value\n\n return target[property]\n\n lastEventListenerId = 0\n eventListeners = {}\n readyPromise = null\n hostTarget =\n ready: ->\n return readyPromise if readyPromise\n\n if remoteExists\n readyPromise = postmaster.send \"ready\",\n ZineOSClient: version\n token: postmaster.token\n .then (hostConfig) ->\n appData = hostConfig?.ZineOS\n\n if appData\n initializeOnZineOS(appData)\n\n return hostConfig\n else \n # Quick resolve when there is no parent window to connect to\n polyfillForStandalone()\n\n readyPromise = Promise.resolve\n standalone: true\n\n # Bind listeners to system events, sending an id in place of a local function\n # reference\n on: (eventName, handler) ->\n lastEventListenerId += 1\n\n eventListeners[lastEventListenerId] = handler\n postmaster.send \"system\", \"on\", eventName, lastEventListenerId\n\n off: (eventName, handler) ->\n [handlerId] = Object.keys(eventListeners).filter (id) ->\n eventListeners[id] is handler\n\n delete eventListeners[handlerId]\n postmaster.send \"system\", \"off\", eventName, handlerId\n\n hostTarget.target = hostTarget\n\n # Unattached, standalone page. Use a systemTarget for that environment\n # Currently mapping system.readFile to fetch\n polyfillForStandalone = ->\n Object.assign hostTarget,\n readFile: (path) ->\n fetch(path)\n .then (response) ->\n if 200 <= response.status < 300\n response.blob()\n else\n throw new Error(response.statusText)\n writeFile: (path, blob) ->\n blob.download(path)\n\n # Proxy to the host environment\n # Host methods can be overridden by writing to the host target\n # this allows us to polyfill for standalone environments (with no host)\n # and provides bindings for event channels and others things (experimental).\n host = new Proxy hostTarget,\n get: (target, property, receiver) ->\n if Object::hasOwnProperty.call(target, property)\n target[property]\n else\n ->\n postmaster.send \"system\", property, arguments...\n\n # TODO: Also interesting would be to proxy observable arguments where we\n # create the receiver on the opposite end of the membrane and pass messages\n # back and forth like magic\n\n initializeOnZineOS = ({id}) ->\n applicationTarget.id = id\n\n document.addEventListener \"mousedown\", ->\n applicationProxy.raiseToTop()\n .catch console.warn\n\n BaseApp = require(\"./app/index\")(host, applicationProxy)\n\n client =\n # `postmaster` makes sense here since it is the client's postmaster instance\n postmaster: postmaster\n\n Object.assign system,\n # Launch stuff\n app:\n Base: BaseApp\n client: client\n config: {} # Host config gets merged into here\n host: host\n\n # Backwards compatible host proxy methods\n # TODO: deprecate?\n readFile: ->\n host.readFile arguments...\n readTree: ->\n host.readTree arguments...\n writeFile: ->\n host.writeFile arguments...\n\n # Only return {system, application}\n # Client utilities can be found in system.client\n # TODO: Remove global `application`\n application: applicationProxy\n system: system\n\nmodule.exports = Runtime\n" }, "lib/test/runtime.coffee": { "content": "mocha.globals(['OBSERVABLE_ROOT_HACK', \"application\"])\n\nsystemG = require \"/lib/exports\"\nSystemClient = require \"../runtime\"\n\nnullLogger =\n info: ->\n debug: ->\n\ndescribe \"Runtime\", ->\n it \"should return system and application proxies\", ->\n {system, application} = SystemClient(systemG)\n\n assert system\n assert application\n \n # Actual API\n app = system.app.Base()\n assert.equal app.currentPath(), \"\"\n assert.equal app.saved(), true\n\n # Cleanup\n system.client.postmaster.dispose()\n\n it \"should queue up messages until a delegate is assigned\", ->\n new Promise (resolve, reject) ->\n {system, application} = SystemClient(systemG)\n\n {postmaster} = system.client\n\n postmaster.delegate.application \"test1\", \"yo\"\n .then (c) ->\n assert.equal c, \"wat\"\n\n postmaster.delegate.application \"test2\", \"yo2\"\n .then (d) ->\n assert.equal d, \"heyy\"\n resolve()\n\n application.delegate =\n test1: (a) ->\n assert.equal a, \"yo\"\n\n return \"wat\"\n\n test2: (b) ->\n assert.equal b, \"yo2\"\n return \"heyy\"\n\n # Cleanup\n system.client.postmaster.dispose()\n\n it \"should connect when ready is called\", (done) ->\n {system, application} = SystemClient(systemG, {\n # logger: console\n })\n\n system.host.ready()\n .then ->\n done()\n system.client.postmaster.dispose()\n\n return\n\n # This was madness to test, the earlier clients had their own postmasters \n # listening!! Make sure to dispose shared resources!\n it \"should launch with config\", ->\n systemG.launch {\n # logger: console\n } , (config) ->\n # Cleanup\n system.client.postmaster.dispose()\n" }, "lib/app/index.coffee": { "content": "# Handle basic file saving/loading/picking, displaying modals/ui. This maps\n# common UI patterns to the `host`'s `readFile` and `writeFile` methods.\n\n# Caller must provide `self` object with the following methods:\n# `loadFile` Take a blob and path and load it as the application state.\n# `saveData` Return a promise that will be fulfilled with a blob of the\n# current application state.\n# `newFile` Initialize the application to an empty state.\n\n# This extends the `self` object with:\n# `currentPath`\n# `drop`\n# `exit`\n# `new`\n# `open`\n# `save`\n# `saved`\n# `saveAs`\n\n# It is expected that there is only one FileIO per page,\n# additional apps spawn in iframes or separate windows for isolation.\n# We add a global drop listener here.\n\n## Events\n#\n# `boot`\n# `dispose`\n#\n# Thought: Would it be better to call these `start` and `finish`?\n\n{applyStyle, Drop, Jadelet, MenuBar, Modal, Observable, Style} = require \"../ui/index\"\n\n# TODO: This is expanding a bit beyond FileIO and into general IO of the\n# app in its environment. We should pass the application proxy in here too and\n# wire it up. The application proxy should also handle the interface to\n# standalone mode things like window.title onbeforeunload behavior, etc.\n\n# Some more thoughts on `application` here... it's not really the right place\n# it needs to be bound to the app after boot when it can listen to the app's\n# observable properties and bind them to window.title, etc.\n\nBindable = require \"../bindable\"\n\n{crudeRequire} = require \"../pkg/index\"\n\nTemplateLoader = require \"./template-loader\"\nHotkeys = require \"./hotkeys\"\n\n# host is used for readFile and writeFile\n# application is used for syncing with the OS App state:\n# - exit\n# - icon\n# - saved\n# - title\n#\n# and setting the delegate on boot to receive messages sent from the host.\n\nmodule.exports = (host, application) ->\n (app={}) ->\n app.saved ?= Observable true\n app.currentPath ?= Observable \"\"\n app.config ?= {}\n\n # Includes\n Bindable null, app\n Hotkeys app\n\n Object.assign app,\n confirmUnsaved: ->\n return Promise.resolve() if app.saved()\n \n new Promise (resolve, reject) ->\n Modal.confirm \"You will lose unsaved progress, continue?\"\n .then (result) ->\n if result\n resolve()\n else\n reject()\n\n exit: ->\n application.exit()\n\n extend: Object.assign.bind(null, app)\n\n # Accepts an array of dropped files\n # returns true if we handled the event\n # apps can override this to customize their behavior\n drop: (files) ->\n file = files[0]\n app.loadFile file, file.name\n return true\n\n # Accepts an array of pasted files\n # returns true if we handled the event\n # apps can override this to customize their behavior\n paste: (files) ->\n file = files[0]\n app.loadFile file, file.name\n return true\n\n new: ->\n if app.saved()\n app.currentPath \"\"\n app.newFile()\n else\n app.confirmUnsaved()\n .then ->\n app.saved true\n app.newFile()\n\n open: ->\n app.confirmUnsaved()\n .then ->\n # TODO: File browser\n # TODO: Delegate to specific strategies\n Modal.prompt \"File Path\", app.currentPath()\n .then (newPath) ->\n if newPath\n app.currentPath newPath\n else\n throw new Error \"No path given\"\n .then (path) ->\n host.readFile path, true\n .then (file) ->\n app.loadFile file, path\n .catch (e) ->\n throw e if e\n\n reloadStyle: (cssText) ->\n applyStyle cssText, \"app\"\n\n save: ->\n path = app.currentPath()\n if path\n Promise.resolve()\n .then ->\n app.saveData()\n .then (blob) ->\n # TODO: Delegate to specific save strategy\n # zineOS, standalone, electron, ...\n # maybe application.writeFile?\n host.writeFile path, blob, true\n .then ->\n app.saved true\n return path\n else\n app.saveAs()\n\n saveAs: ->\n Modal.prompt \"File Path\", app.currentPath()\n .then (path) ->\n if path\n app.currentPath path\n app.save()\n\n # Detecting standalone config flag and provide alternative open and save\n # methods\n if system.config?.standalone\n ReaderInput = require \"../../templates/reader-input\"\n\n # Override chooser to use local PC\n app.open = ->\n Modal.show ReaderInput\n accept: app.accept?()\n select: (file) ->\n Modal.hide()\n app.loadFile file\n \n # Override save to present download\n app.save = ->\n Modal.prompt \"File name\", \"newfile.txt\"\n .then (name) ->\n app.saveData()\n .then (blob) ->\n blob.download()\n\n # Provide drop event\n # TODO: Remove drop handlers on dispose\n Drop document, (e) ->\n return if e.defaultPrevented\n\n files = e.dataTransfer.files\n\n if files.length\n e.preventDefault() if app.drop files\n\n # Provide paste event\n # TODO: Remove paste handlers on dispose\n document.addEventListener \"paste\", (e) ->\n return if e.defaultPrevented\n \n {clipboardData} = e\n \n files = clipboardData.files\n\n if files.length\n if app.paste files\n return e.preventDefault()\n\n files = Array::map.call e.clipboardData.items, (item) ->\n item.getAsFile()\n .filter (file) -> file\n\n if files.length\n e.preventDefault() if app.paste files\n\n try\n app.T ?= {}\n TemplateLoader app.pkg, app.T\n\n try\n app.version = crudeRequire(app.pkg.distribution.pixie.content).version\n\n # `boot` triggers\n app.on \"boot\", ->\n # Auto-apply base and app styles\n unless app.config.baseStyle is false\n applyStyle Style.all, \"base\"\n if @style\n applyStyle @style, \"app\"\n else\n try\n applyStyle crudeRequire(app.pkg.distribution.style.content), \"app\"\n\n # Auto-menu from menu string\n if @menu\n menuBar = MenuBar\n items: @menu\n handlers: @\n\n document.body.appendChild menuBar.element\n app.on \"dispose\", ->\n menuBar.element.remove()\n Jadelet.dispose menuBar.element\n\n if @element\n document.body.appendChild @element\n else if @template\n @element = Jadelet.exec(@template)(this)\n document.body.appendChild @element\n else if @T.App\n @element = @T.App this\n document.body.appendChild @element\n\n # Bind host application pieces\n application.delegate = @\n # auto-bind application title\n # Pipes title changes to os application window, etc.\n if @title?\n Observable -> application.title getProp app, \"title\"\n\n if @icon?\n Observable ->\n application.icon getProp app, \"icon\"\n\n # Pipe saved state to os app state\n if @saved?\n Observable -> application.saved getProp app, \"saved\"\n\n # TODO: onbeforeunload?\n\n app.on \"dispose\", ->\n if @element\n @element.remove()\n Jadelet.dispose @element\n\n return app\n\ngetProp = (context, prop) ->\n if typeof context[prop] is 'function'\n context[prop]()\n else\n context[prop]\n" }, "lib/test/app/index.coffee": { "content": "{Observable} = require \"/lib/ui/index\"\n\nAppGen = require \"/lib/app/index\"\n\nappProxyMock = {}\n\nBaseApp = AppGen({}, appProxyMock)\n\ndescribe \"App\", ->\n it \"should provide a base app constructor\", ->\n assert BaseApp()\n\n it \"should work standalone\", ->\n do (oldSystem=system) ->\n global.system =\n config:\n standalone: true\n\n assert BaseApp()\n\n global.system = oldSystem\n\n it \"should add a hotkey\", ->\n app = BaseApp()\n\n called = 0\n app.hotkey \"a\", (e) ->\n called++\n\n e = new KeyboardEvent('keypress', {keyCode: 97})\n document.dispatchEvent e\n\n assert.equal called, 1\n\n it \"should extend\", ->\n app = BaseApp()\n\n app.extend\n cool: \"duder\"\n\n assert.equal app.cool, \"duder\"\n\n it \"should include bindable\", ->\n app = BaseApp()\n\n assert app.on\n assert app.off\n assert app.trigger\n\n it \"should set title and icon\", ->\n saved = icon = title = null\n\n appProxyMock.title = (_title) ->\n title = _title\n appProxyMock.icon = (_icon) ->\n icon = _icon\n appProxyMock.saved = (_saved) ->\n saved = _saved\n\n app = BaseApp\n title: \"yolo\"\n icon: \"R\"\n saved: false\n\n app.trigger 'boot'\n \n assert.equal icon, \"R\"\n assert.equal title, \"yolo\"\n assert.equal saved, false\n\n it \"should pass on observable title changes\", ->\n title = null\n\n appProxyMock.title = (_title) ->\n title = _title\n\n app = BaseApp\n title: Observable \"wat\"\n\n app.trigger 'boot'\n assert.equal title, \"wat\"\n\n app.title \"cool\"\n assert.equal title, \"cool\"\n\n it \"should apply app template by default on boot and remove on dispose\", ->\n app = BaseApp\n T:\n App: system.ui.Jadelet.exec \"app Hello\"\n menu: \"\"\"\n Hello\n Wat\n \"\"\"\n\n app.trigger 'boot'\n assert document.querySelector 'app'\n app.trigger 'dispose'\n assert !document.querySelector('app')\n" }, "lib/app/template-loader.coffee": { "content": "{crudeRequire} = require \"../pkg/index\"\n\nmodule.exports = (pkg, templates={}) ->\n Object.keys(pkg.distribution).forEach (key) ->\n if key.startsWith 'templates/'\n templateName = key\n .replace(/^templates\\//, \"\")\n .replace(/^([a-z])|[_-]([a-z])/g, (m, a, b) ->\n (a or b).toUpperCase()\n )\n\n try\n templates[templateName] = crudeRequire pkg.distribution[key].content\n catch e\n console.warn e\n\n return templates\n" }, "lib/test/app/template-loader.coffee": { "content": "require \"/setup\"\nTemplateLoader = require \"/lib/app/template-loader\"\n\ndescribe \"template loader\", ->\n it \"should load templates\", ->\n tl = TemplateLoader(PACKAGE)\n\n assert tl.Progress\n" }, "lib/aws/index.coffee": { "content": "###\nInterface to all our AWS madness.\n\n###\n\n{urlSafeSHA256} = require \"../util/index\"\n\nLL = require \"./_lazy\"\n\nmodule.exports =\n Cognito: require \"./cognito\"\n\n # requires that the user has been authorized with Cognito\n # TODO: catch and refresh credentials\n api: LL (path, params={}) ->\n url = new URL \"https://api.whimsy.space/#{path}\"\n url.searchParams.append \"idpjwt\", Object.values(AWS.config.credentials.params.Logins)[0]\n\n if params.body?\n params.body = JSON.stringify params.body\n\n fetch url, params\n\n # Requires that the user has been authorized with Cognito\n cdn: LL (blob) ->\n S3 = new AWS.S3\n params:\n Bucket: \"whimsy-fs\"\n\n S3.config.credentials = AWS.config.credentials\n id = AWS.config.credentials.identityId\n\n queryExisting = (sha) ->\n fetch \"https://whimsy.space/cdn/#{sha}\",\n method: 'HEAD'\n .then (response) ->\n response.status is 200\n\n # Compute urlsafe sha256\n urlSafeSHA256(blob)\n .then (sha) ->\n queryExisting(sha)\n .then (found) ->\n return sha if found\n\n # Post to whimsy-fs/incoming/user-id/sha\n S3.putObject\n Key: \"incoming/#{id}/#{sha}\"\n ContentType: blob.type\n Body: blob\n .promise()\n .then ->\n # Gently poll whimsy.space/cdn/sha\n # reslove when available\n new Promise (resolve, reject) ->\n timeout = 1000\n n = 0\n\n check = ->\n n += 1\n\n if n <= 10\n queryExisting(sha)\n .then (found) ->\n if found\n resolve(sha)\n else\n setTimeout ->\n check()\n , timeout\n else\n reject()\n\n check()\n\n # Open an authenticated websocket connection to the whimsy.space server\n ws: ->\n url = new URL \"wss://ws.whimsy.space/\"\n url.searchParams.append \"idpjwt\", Object.values(AWS.config.credentials.params.Logins)[0]\n\n new WebSocket url\n\n # Resolve with true if lazy loading succeeded\n ready: LL ->\n AWS\n" }, "lib/test/aws/index.coffee": { "content": "require \"/lib/extensions\"\n\n{api, cdn, ready} = require \"/lib/aws/index\"\n\nCognito = require \"/lib/aws/cognito\"\n\n{cognito:config} = PACKAGE.config\ncognito = Cognito(config)\n\nmocha.setup\n globals: ['AWSCognito', 'AmazonCognitoIdentity', 'AWS']\n\n# skipped for test performance, dependence on remote resources\ndescribe.skip \"AWS\", ->\n it \"cdn\", ->\n @timeout 30000\n blob = new Blob [\"heyy234\"], type: \"text/plain\"\n\n cognito.authenticate(\"daniel+test@danielx.net\", \"yo yo yo\")\n .then ->\n cdn blob\n\n it \"api\", ->\n @timeout 5000\n\n cognito.authenticate(\"daniel+test@danielx.net\", \"yo yo yo\")\n .then ->\n api(\"\")\n\n it \"ready\", ->\n ready()\n" }, "lib/util/index.coffee": { "content": "# Load scripts sequentially, prevents failures if there is a dependency\n# order\nloadScripts = (urls) ->\n urls.reduce (p, url) ->\n p.then ->\n # Resolve if present\n if document.querySelector \"script[src=#{JSON.stringify(url)}]\"\n return Promise.resolve()\n\n script = document.createElement \"script\"\n script.src = url\n document.body.appendChild script\n\n return new Promise (resolve, reject) ->\n script.onload = resolve\n script.onerror = reject\n , Promise.resolve()\n\n\n###\nCopy a string to user's OS (win,mac,linux) clipboard.\n###\ncopyToClipboard = (str) ->\n el = document.createElement 'textarea'\n el.value = str\n el.setAttribute 'readonly', ''\n el.style.position = 'absolute' \n el.style.left = '-9999px'\n document.body.appendChild el\n\n el.select()\n document.execCommand('copy')\n\n document.body.removeChild(el)\n\nbufferToBase64 = (buffer) ->\n window.btoa String.fromCharCode.apply null, new Uint8Array buffer\n\nbase64URLEncode = (base64String) ->\n base64String.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/\\=/g, \"\")\n\ndigest = (data) ->\n crypto.subtle.digest(\"SHA-256\", data)\n\nescapeRegex = (string) ->\n string.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n\n# Match is an array of regex match results\n# unmatched runs of characters followed by single character matches, alternating\n# [\"\", \"a\", \" cool \", \"d\", \"og\"]\n# returns a score, higher is better\nscoreMatch = (match) ->\n return unless match\n\n # -1 so that if first char is matched it gets consecutive bonus\n lastMatch = -1\n pos = 0\n match.slice(1).reduce (score, s, i) ->\n if s.length is 0\n value = 0\n else\n # Unmatched run\n if i % 2 is 0\n value = -s.length\n else # Matched char\n # Consecutive\n if pos is 1 + lastMatch\n lastMatch = pos\n value = 8\n else\n value = 2\n\n # console.log \"Score: #{score}, s: '#{s}', v: #{value}\"\n pos += s.length\n return score + value\n , 0\n\nmodule.exports =\n copyToClipboard: copyToClipboard\n deprecationWarning: (msg, fn) ->\n if typeof msg is \"function\"\n fn = msg\n msg = \"DEPRECATED\"\n\n ->\n console.warn msg\n fn.apply(this, arguments)\n\n escapeRegex: escapeRegex\n\n fuzzyMatch: (term, items, asString=String) ->\n re = RegExp(\"^\" +term.split(\"\").map (c) ->\n \"([^#{escapeRegex c}]*)(#{escapeRegex c})\"\n .join('') + '(.*)$', \"i\")\n\n items.map (item) ->\n if match = asString(item).match re\n [item, scoreMatch match]\n .filter (result) -> result?\n .sort (a, b) ->\n b[1] - a[1]\n .map (result) ->\n result[0]\n\n groupBy: (array, fn) ->\n array.reduce (result, item) ->\n (result[fn(item)] ?= []).push item\n\n return result\n , {}\n\n loadScripts: loadScripts\n\n # Takes an array of urls, returns a decorator that checks the deps have resolved\n # before invoking the given function\n lazyLoader: (urls) ->\n # Load the dependencies keeping a promise to limit to only one request\n # clearing the limit on failure\n # caching on success\n loadingDeps = null\n _load = ->\n if loadingDeps\n return loadingDeps\n\n loadingDeps = loadScripts(urls).catch (e) ->\n console.error e\n loadingDeps = null\n throw e\n\n # Decorator to ensure initialized\n return (fn) ->\n (args...) ->\n context = this\n _load().then ->\n fn.apply context, args\n\n Postmaster: require \"../postmaster\"\n\n # Limit promise requests with the same key to only one in flight\n promiseChoke: (fn) ->\n cache = {}\n \n (key) ->\n cached = cache[key]\n if cached\n return cached\n \n cache[key] = fn(key).finally ->\n delete cache[key]\n\n throttle: (wait, func) ->\n context = args = result = undefined\n timeout = null\n previous = 0\n\n later = ->\n previous = Date.now()\n timeout = null\n result = func.apply(context, args)\n if !timeout\n context = args = null\n\n return ->\n now = Date.now()\n remaining = wait - (now - previous)\n context = this\n args = arguments\n if remaining <= 0 || remaining > wait\n if timeout\n clearTimeout(timeout)\n timeout = null\n\n previous = now\n result = func.apply(context, args)\n if (!timeout)\n context = args = null\n else if !timeout\n timeout = setTimeout(later, remaining)\n\n return result\n\n urlSafeSHA256: (blob) ->\n blob.arrayBuffer()\n .then digest\n .then bufferToBase64\n .then base64URLEncode\n" }, "lib/test/util/index.coffee": { "content": "require \"/lib/extensions\"\n{\n copyToClipboard\n deprecationWarning\n fuzzyMatch\n groupBy\n lazyLoader\n promiseChoke\n throttle\n urlSafeSHA256\n} = require \"/lib/util/index\"\n\ndescribe \"util\", ->\n describe \"copyToClipboard\", ->\n it \"should copy a string to system clipboard\", ->\n copyToClipboard \"yolo\"\n\n describe \"deprecationWarning\", ->\n it \"should display an error when calling a deprecated function\", ->\n yolo = (x, y) ->\n x + y\n\n yolo = deprecationWarning \"util.yolo is deprecated use based.swag instead\", yolo\n\n # Mock console\n do (warn=console.warn) ->\n called = false\n console.warn = (msg) ->\n called = true\n assert.equal msg, \"util.yolo is deprecated use based.swag instead\"\n assert.equal yolo(5, 3), 8\n console.warn = warn\n assert called\n\n describe \"fuzzyMatch\", ->\n it \"should match fuzzily\", ->\n assert fuzzyMatch \"\", [\"\"]\n assert fuzzyMatch \"\", [\"a\"]\n assert fuzzyMatch \"a\", [\"a\"]\n\n result = fuzzyMatch(\"acd\", [\n \"a gcac\"\n \"a cool dog\"\n \"achieved\"\n \"yoro\"\n \"what act duder\"\n ])\n\n assert.equal result.length, 3\n\n\n describe \"groupBy\", ->\n it \"should group arrays by fn\", ->\n a = [1, 2, 3, 4, 5, 6, 7, 8, 9]\n result = groupBy a, (n) ->\n n % 3\n\n assert.deepEqual result[0], [3, 6, 9]\n assert.deepEqual result[1], [1, 4, 7]\n assert.deepEqual result[2], [2, 5, 8]\n\n describe \"lazyLoader\", ->\n it \"should lazy load\", ->\n LL = lazyLoader([])\n \n a = (x) -> x\n b = LL a\n \n b(0).then (x) ->\n assert.equal x, 0\n\n describe \"promiseChoke\", ->\n it \"should limit promise returning function execution to one at a time\", ->\n called = 0\n fn = promiseChoke ->\n called++\n new Promise (resolve) ->\n setTimeout resolve\n\n fn()\n fn()\n fn().then ->\n assert.equal called, 1\n\n it \"should reset on error\", ->\n called = 0\n fn = promiseChoke ->\n called++\n new Promise (resolve, reject) ->\n setTimeout reject\n\n fn()\n fn()\n fn().catch ->\n assert.equal called, 1\n fn().catch ->\n assert.equal called, 2\n\n it \"should key off of first argument\", ->\n called = 0\n fn = promiseChoke (x) ->\n called++\n new Promise (resolve) ->\n setTimeout ->\n resolve x\n\n fn(1)\n fn(2)\n fn(5).then (v) ->\n assert.equal v, 5\n assert.equal called, 3\n\n describe \"throttle\", ->\n it \"should be called no more than once per time block\", (done) ->\n\n called = 0\n\n f = throttle 15, -> called += 1\n\n f()\n f()\n f()\n\n setTimeout ->\n f()\n\n setTimeout ->\n f()\n , 5\n\n setTimeout ->\n f()\n , 10\n\n setTimeout ->\n if called != 2\n done new Error \"Should have been called twice\"\n else\n done()\n , 20\n\n describe \"urlSafeSHA256\", ->\n it \"should create a URL safe SHA256 for the blob\", ->\n urlSafeSHA256(new Blob [\"yolo\"])\n .then (str) ->\n assert.equal str, \"MR_j_u0Wuc2N8PixUXvly4YEhwffSIm6jcN9TWiGbQI\"\n" }, "lib/pkg/index.coffee": { "content": "###\n`pkg` holds utilities for bundling and launching packages as standalone blobs or\nin iframes.\n\n###\n\nhtmlToBlob = (htmlString) ->\n new Blob [htmlString], type: \"text/html; charset=utf-8\"\n\nmetaTag = (name, content) ->\n \"\"\n\nlinkTag = (rel, href) ->\n \"\"\n\n{lazyLoader} = require \"../util/index\"\n\nuglifyLoaded = lazyLoader [\"https://danielx.net/cdn/uglify/3.0.0.min.js\"]\n\n###\nConstruct an HTML file for the package.\n\nThe default behavior is to load the package's entry point but\nthat can be modified enter from any file in the package.\n\nIt also adds remote dependencies to the HTML head and wraps with\nthe system launch if present.\n\nThis is designed to be simple and general, any magic binding should happen in\nthe `!system` layer. Here we are only concerned with html tags and setting up\nthe package with `require`.\n\n`opts`\nadditionalDependencies: array of additional dependency scripts to include in\nthe html source. Useful for things like testing libraries, doc formatting, etc.\ncode: The code snippet to run, defaults to requiring the default package\nentry point.\nstylesheets: array of urls to add as stylesheet link tags\nsystemConfig: configuration parameters for the system runtime.\n###\nhtmlForPackage = (pkg, opts={}) ->\n metas = [\n ''\n ''\n ]\n\n {config, progenitor} = pkg\n config ?= {}\n\n {code, systemConfig} = opts\n\n # by default launch from the packages entry point or main file.\n code ?= \"\"\"\n require('./#{pkg.entryPoint or \"main\"}');\n \"\"\"\n code = systemWrap(pkg, code, systemConfig)\n\n {title, description, lang, iconURL, manifest} = config\n\n if lang\n langFragment = \" lang=#{JSON.stringify(lang)}\"\n else\n langFragment = \"\"\n\n if title\n metas.push \"#{title}\"\n\n if description\n metas.push metaTag \"description\", description.replace(\"\\n\", \" \")\n\n if iconURL\n metas.push linkTag \"shortcut icon\", iconURL\n\n if manifest\n metas.push linkTag \"manifest\", \"./manifest.webmanifest\"\n\n # Progenitor link can be used to for a built-in \"Edit this!\" feature\n # TODO: Should url be href?\n url = pkg.progenitor?.url\n if url\n metas.push linkTag \"progenitor\", url\n\n (pkg.stylesheets || []).concat(opts.stylesheets || []).forEach (href) ->\n metas.push linkTag \"stylesheet\", href\n\n htmlToBlob \"\"\"\n \n \n \n #{metas.join(\"\\n \")}\n #{dependencyScripts(opts.additionalDependencies, pkg.remoteDependencies)}\n \n \n \\n\");\n};\n\ndependencyScripts = function(additionalDependencies, remoteDependencies) {\n if (additionalDependencies == null) {\n additionalDependencies = [];\n }\n if (remoteDependencies == null) {\n remoteDependencies = [];\n }\n return additionalDependencies.concat(remoteDependencies).map(function(src) {\n return \"

  1. Yolo
  2. Broheim

\";\n return div;\n }\n };\n element = template(model);\n assert(element.querySelectorAll(\"li\").length, 2);\n assert(element.querySelectorAll(\"p\").length, 1);\n return assert(element.querySelectorAll(\"ol\").length, 1);\n });\n return it(\"should work with observables\", function() {\n var element, model;\n model = {\n name: Observable(\"wat\"),\n generateItem: function() {\n var item;\n item = document.createElement(\"li\");\n item.textContent = this.name();\n return item;\n }\n };\n element = template(model);\n assert.equal(element.querySelectorAll(\"li\").length, 1);\n assert.equal(element.querySelector(\"li\").textContent, \"wat\");\n model.name(\"yo\");\n return assert.equal(element.querySelector(\"li\").textContent, \"yo\");\n });\n });\n return describe(\"rendering subtemplates\", function() {\n describe(\"mixing and matching\", function() {\n var subtemplate, template;\n subtemplate = makeTemplate(\"span Hello\");\n template = makeTemplate(\"div\\n a Radical\\n |\\n @subtemplate\\n |\\n @observable\\n @nullable\");\n return it(\"shouldn't lose any nodes\", function() {\n var element, model;\n model = {\n observable: Observable(\"wat\"),\n subtemplate: subtemplate,\n nullable: null\n };\n element = template(model);\n assert.equal(element.textContent, \"Radical\\nHello\\nwat\");\n model.observable(\"duder\");\n return assert.equal(element.textContent, \"Radical\\nHello\\nduder\");\n });\n });\n return describe(\"mapping array to subtemplates\", function() {\n var template;\n template = makeTemplate(\"table\\n @rows\");\n it(\"should render subtemplates\", function() {\n var element, model;\n model = {\n rows: function() {\n return [\n {\n text: \"Wat\"\n }, {\n text: \"is\"\n }, {\n text: \"up\"\n }\n ].map(this.subtemplate);\n },\n subtemplate: makeTemplate(\"tr\\n td @text\")\n };\n element = template(model);\n return assert.equal(element.querySelectorAll(\"tr\").length, 3);\n });\n return it(\"should maintain observables in subtemplates\", function() {\n var data, element, model;\n data = Observable([\n {\n text: Observable(\"Wat\")\n }, {\n text: Observable(\"is\")\n }, {\n text: Observable(\"up\")\n }\n ]);\n model = {\n rows: function() {\n return data.map(this.subtemplate);\n },\n subtemplate: makeTemplate(\"tr\\n td @text\")\n };\n element = template(model);\n assert.equal(element.querySelectorAll(\"tr\").length, 3);\n assert.equal(element.querySelector(\"td\").textContent, \"Wat\");\n data()[0].text(\"yo\");\n assert.equal(element.querySelector(\"td\").textContent, \"yo\");\n data.push({\n text: Observable(\"dude\")\n });\n assert.equal(element.querySelectorAll(\"tr\").length, 4);\n assert.equal(element.querySelector(\"td\").textContent, \"yo\");\n data()[0].text(\"holla\");\n return assert.equal(element.querySelector(\"td\").textContent, \"holla\");\n });\n });\n });\n});\n\ndescribe(\"element properties\", function() {\n return it(\"should use the element property of objects rendered\", function() {\n var element, src, template;\n src = \"ul\\n @a\\n @b\";\n template = makeTemplate(src);\n element = template({\n a: {\n element: document.createElement('a')\n },\n b: {\n element: document.createElement('b')\n }\n });\n assert(element.querySelector(\"a\"));\n assert(element.querySelector(\"b\"));\n return assert(!element.querySelector(\"c\"));\n });\n});\n\ndescribe(\"cached retains\", function() {\n return it(\"should not release elements that are consistent render to render\", function() {\n var Container, Item, ItemView, cache, containerView, element, itemViews;\n Container = makeTemplate(\"div\\n @elements\");\n Item = makeTemplate(\"input(@value)\");\n ItemView = function(i) {\n var item, view;\n view = cache.get(i);\n if (view) {\n return view;\n }\n item = {\n value: Observable(i)\n };\n view = {\n item: item,\n element: Item(item)\n };\n cache.set(i, view);\n return view;\n };\n cache = new Map;\n itemViews = [0, 1, 2].map(ItemView);\n containerView = {\n elements: Observable(itemViews)\n };\n element = Container(containerView);\n assert.equal(element.children.length, 3);\n containerView.elements()[0].item.value(100);\n assert.equal(element.children[0].value, 100);\n containerView.elements.push(ItemView(3));\n assert.equal(element.children.length, 4);\n containerView.elements()[0].item.value(200);\n return assert.equal(element.children[0].value, 200);\n });\n});\n\ndescribe(\"text\", function() {\n return it(\"should preserve line breaks\", function() {\n var element, src, template;\n src = \"p\\n | hello I am a cool paragraph\\n | with lots of text and stuff\\n | ain't it rad?\";\n template = makeTemplate(src);\n element = template();\n return assert.equal(element.textContent, \"hello I am a cool paragraph\\nwith lots of text and stuff\\nain't it rad?\\n\");\n });\n});\n\ndescribe(\"svg\", function() {\n return it(\"should render svg\", function() {\n var element, src, template;\n src = \"section\\n h2 svg test\\n svg(width=100 height=100)\\n circle(cx=80 cy=80 r=30 fill=\\\"red\\\")\\n p awesome\";\n template = makeTemplate(src);\n element = template();\n return assert.equal(element.querySelector('svg').namespaceURI, \"http://www.w3.org/2000/svg\");\n });\n});\n\ndescribe(\"indentation\", function() {\n return it(\"should work with somewhat flexible indentation for ease of use with template strings in js\", function() {\n var T1, T2, el, indentedTemplate1, indentedTemplate2;\n indentedTemplate1 = \"p\\n a(@click) Cool\";\n indentedTemplate2 = indentedTemplate1.replace(/^/, \" \");\n T1 = makeTemplate(indentedTemplate1);\n T2 = makeTemplate(indentedTemplate2);\n el = T1();\n assert.equal(el.querySelector('a').textContent, \"Cool\");\n el = T2();\n return assert.equal(el.querySelector('a').textContent, \"Cool\");\n });\n});\n\ndescribe(\"weird cases\", function() {\n return it.skip(\"should handle weird templates\", function() {\n return makeTemplate('.palette\\n .primary.color\\n - style = ->\\n - c = editor.activeColor()\\n - \"background-color: #{c}\"\\n input(type=\"color\" value=@activeColor style=@activeColorStyle)\\n\\n @swatchElements\\n @opacityElement');\n });\n});\n" }, "lib/test/mousetrap": { "content": "\n" }, "lib/test/observable": { "content": "var Observable;\n\nObservable = require(\"../observable\");\n\ndescribe('Observable', function() {\n it('should create an observable for an object', function() {\n var n, observable;\n n = 5;\n observable = Observable(n);\n return assert.equal(observable(), n);\n });\n it('should fire events when setting', function() {\n var observable, string;\n string = \"yolo\";\n observable = Observable(string);\n observable.observe(function(newValue) {\n return assert.equal(newValue, \"4life\");\n });\n return observable(\"4life\");\n });\n it(\"should not fire when setting to the same value\", function() {\n var o;\n o = Observable(5);\n o.observe(function() {\n return assert(false);\n });\n return o(5);\n });\n it('should be idempotent', function() {\n var o;\n o = Observable(5);\n return assert.equal(o, Observable(o));\n });\n it(\"should have releaseDependencies as a noop because primitive observables don't have any dependencies\", function() {\n var o;\n o = Observable(5);\n return o.releaseDependencies();\n });\n it(\"should provide an updating non-observing semi-private reference to value\", function() {\n var o, o2, o3;\n o = Observable(5);\n assert.equal(o._value, 5);\n o(7);\n assert.equal(o._value, 7);\n o2 = Observable(function() {\n return o._value;\n });\n o3 = Observable(function() {\n return o();\n });\n assert.equal(o2(), 7);\n assert.equal(o3(), 7);\n o(9);\n assert.equal(o2(), 7);\n assert.equal(o2._value, 7);\n assert.equal(o3(), 9);\n assert.equal(o3._value, 9);\n return assert.equal(o._value, 9);\n });\n it(\"should allow for stopping observation\", function() {\n var called, fn, observable;\n observable = Observable(\"string\");\n called = 0;\n fn = function(newValue) {\n called += 1;\n return assert.equal(newValue, \"4life\");\n };\n observable.observe(fn);\n observable(\"4life\");\n observable.stopObserving(fn);\n observable(\"wat\");\n return assert.equal(called, 1);\n });\n it(\"should do nothing when removing a listener that's not present\", function() {\n var observable;\n observable = Observable(\"string\");\n return observable.stopObserving(function() {});\n });\n it(\"should increment\", function() {\n var observable;\n observable = Observable(1);\n observable.increment(5);\n assert.equal(observable(), 6);\n observable.increment();\n assert.equal(observable(), 7);\n observable.increment(0.05);\n assert.equal(observable(), 7.05);\n observable.increment(0.05);\n return assert.equal(observable(), 7.10);\n });\n it(\"should decremnet\", function() {\n var observable;\n observable = Observable(1);\n observable.decrement(5);\n assert.equal(observable(), -4);\n observable.decrement();\n return assert.equal(observable(), -5);\n });\n it(\"should toggle\", function() {\n var observable;\n observable = Observable(false);\n observable.toggle();\n assert.equal(observable(), true);\n observable.toggle();\n return assert.equal(observable(), false);\n });\n it(\"should trigger when toggling\", function(done) {\n var observable;\n observable = Observable(true);\n observable.observe(function(v) {\n assert.equal(v, false);\n return done();\n });\n return observable.toggle();\n });\n return it(\"should have a nice toString\", function() {\n var observable;\n observable = Observable(5);\n return assert.equal(observable.toString(), \"Observable(5)\");\n });\n});\n\ndescribe(\"Observable Array\", function() {\n it(\"should proxy array methods\", function() {\n var o;\n o = Observable([5]);\n return o.map(function(n) {\n return assert.equal(n, 5);\n });\n });\n it(\"should notify on mutation methods\", function(done) {\n var o;\n o = Observable([]);\n o.observe(function(newValue) {\n return assert.equal(newValue[0], 1);\n });\n o.push(1);\n return done();\n });\n it(\"#get\", function() {\n var o;\n o = Observable([0, 1, 2, 3]);\n return assert.equal(o.get(2), 2);\n });\n it(\"#first\", function() {\n var o;\n o = Observable([0, 1, 2, 3]);\n return assert.equal(o.first(), 0);\n });\n it(\"#last\", function() {\n var o;\n o = Observable([0, 1, 2, 3]);\n return assert.equal(o.last(), 3);\n });\n it(\"#remove\", function() {\n var o;\n o = Observable([0, 1, 2, 3]);\n assert.equal(o.remove(2), 2);\n assert.equal(o.length, 3);\n assert.equal(o.remove(-5), void 0);\n return assert.equal(o.length, 3);\n });\n it(\"#remove non-existent element\", function() {\n var o;\n o = Observable([1, 2, 3]);\n return assert.equal(o.remove(0), void 0);\n });\n it(\"should proxy the length property\", function() {\n var called, o;\n o = Observable([1, 2, 3]);\n assert.equal(o.length, 3);\n called = false;\n o.observe(function(value) {\n assert.equal(value[0], 1);\n assert.equal(value[1], void 0);\n return called = true;\n });\n o.length = 1;\n assert.equal(o.length, 1);\n return assert.equal(called, true);\n });\n return it(\"should auto detect conditionals of length as a dependency\", function() {\n var called, o, observableArray;\n observableArray = Observable([1, 2, 3]);\n o = Observable(function() {\n if (observableArray.length > 5) {\n return true;\n } else {\n return false;\n }\n });\n assert.equal(o(), false);\n called = 0;\n o.observe(function() {\n return called += 1;\n });\n observableArray.push(4, 5, 6);\n return assert.equal(called, 1);\n });\n});\n\ndescribe(\"Observable functions\", function() {\n it(\"should compute dependencies\", function(done) {\n var firstName, lastName, o;\n firstName = Observable(\"Duder\");\n lastName = Observable(\"Man\");\n o = Observable(function() {\n return \"\" + (firstName()) + \" \" + (lastName());\n });\n o.observe(function(newValue) {\n assert.equal(newValue, \"Duder Bro\");\n return done();\n });\n return lastName(\"Bro\");\n });\n it(\"should compute array#get as a dependency\", function() {\n var observableArray, observableFn;\n observableArray = Observable([0, 1, 2]);\n observableFn = Observable(function() {\n return observableArray.get(0);\n });\n assert.equal(observableFn(), 0);\n observableArray([5]);\n return assert.equal(observableFn(), 5);\n });\n it(\"should compute array#first as a dependency\", function() {\n var observableArray, observableFn;\n observableArray = Observable([0, 1, 2]);\n observableFn = Observable(function() {\n return observableArray.first() + 1;\n });\n assert.equal(observableFn(), 1);\n observableArray([5]);\n return assert.equal(observableFn(), 6);\n });\n it(\"should compute array#last as a dependency\", function() {\n var observableArray, observableFn;\n observableArray = Observable([0, 1, 2]);\n observableFn = Observable(function() {\n return observableArray.last();\n });\n assert.equal(observableFn(), 2);\n observableArray.pop();\n assert.equal(observableFn(), 1);\n observableArray([5]);\n return assert.equal(observableFn(), 5);\n });\n it(\"should compute array#size as a dependency\", function() {\n var observableArray, observableFn;\n observableArray = Observable([0, 1, 2]);\n observableFn = Observable(function() {\n return observableArray.size() * 2;\n });\n assert.equal(observableFn(), 6);\n observableArray.pop();\n assert.equal(observableFn(), 4);\n observableArray.shift();\n return assert.equal(observableFn(), 2);\n });\n it(\"should allow double nesting\", function(done) {\n var bottom, middle, top;\n bottom = Observable(\"rad\");\n middle = Observable(function() {\n return bottom();\n });\n top = Observable(function() {\n return middle();\n });\n top.observe(function(newValue) {\n assert.equal(newValue, \"wat\");\n assert.equal(top(), newValue);\n assert.equal(middle(), newValue);\n return done();\n });\n return bottom(\"wat\");\n });\n it(\"should work with dynamic dependencies\", function() {\n var dynamicObservable, observableArray;\n observableArray = Observable([]);\n dynamicObservable = Observable(function() {\n return observableArray.filter(function(item) {\n return item.age() > 3;\n });\n });\n assert.equal(dynamicObservable().length, 0);\n observableArray.push({\n age: Observable(1)\n });\n observableArray()[0].age(5);\n return assert.equal(dynamicObservable().length, 1);\n });\n it(\"should work with context\", function() {\n var model;\n model = {\n a: Observable(\"Hello\"),\n b: Observable(\"there\")\n };\n model.c = Observable(function() {\n return \"\" + (this.a()) + \" \" + (this.b());\n }, model);\n assert.equal(model.c(), \"Hello there\");\n model.b(\"world\");\n return assert.equal(model.c(), \"Hello world\");\n });\n it(\"should be ok even if the function throws an exception\", function() {\n assert.throws(function() {\n var t;\n return t = Observable(function() {\n throw \"wat\";\n });\n });\n return assert.equal(global.OBSERVABLE_ROOT_HACK.length, 0);\n });\n it(\"should work on an array dependency\", function() {\n var last, o, oA;\n oA = Observable([1, 2, 3]);\n o = Observable(function() {\n return oA()[0];\n });\n last = Observable(function() {\n return oA()[oA().length - 1];\n });\n assert.equal(o(), 1);\n oA.unshift(0);\n assert.equal(o(), 0);\n oA.push(4);\n return assert.equal(last(), 4, \"Last should be 4\");\n });\n it(\"should work with multiple dependencies\", function() {\n var checked, first, letter, second;\n letter = Observable(\"A\");\n checked = function() {\n var l;\n l = letter();\n return this.name().indexOf(l) === 0;\n };\n first = {\n name: Observable(\"Andrew\")\n };\n first.checked = Observable(checked, first);\n second = {\n name: Observable(\"Benjamin\")\n };\n second.checked = Observable(checked, second);\n assert.equal(first.checked(), true);\n assert.equal(second.checked(), false);\n assert.equal(letter.listeners.length, 2);\n letter(\"B\");\n assert.equal(first.checked(), false);\n return assert.equal(second.checked(), true);\n });\n it(\"shouldn't double count dependencies\", function() {\n var count, dep, o;\n dep = Observable(\"yo\");\n o = Observable(function() {\n dep();\n dep();\n return dep();\n });\n count = 0;\n o.observe(function() {\n return count += 1;\n });\n dep('heyy');\n return assert.equal(count, 1);\n });\n it(\"should recompute the correct number of times\", function() {\n var called, fn, items, joiner;\n joiner = Observable(\",\");\n items = Observable([\"A\", \"B\", \"C\"]);\n called = 0;\n fn = Observable(function() {\n called += 1;\n return items.join(joiner());\n });\n assert.equal(fn(), \"A,B,C\");\n assert.equal(called, 1);\n items.push(\"D\");\n assert.equal(fn(), \"A,B,C,D\");\n assert.equal(called, 2);\n joiner(\".\");\n assert.equal(fn(), \"A.B.C.D\");\n assert.equal(called, 3);\n items.push(\"E\");\n assert.equal(fn(), \"A.B.C.D.E\");\n return assert.equal(called, 4);\n });\n it(\"should work with nested observable construction\", function() {\n var gen, o, o2;\n gen = Observable(function() {\n return Observable(\"Duder\");\n });\n o = gen();\n o2 = gen();\n assert.equal(o, o2);\n assert.equal(o(), \"Duder\");\n o(\"wat\");\n return assert.equal(o(), \"wat\");\n });\n return it(\"should be scoped to optional context\", function(done) {\n var model;\n model = {\n firstName: Observable(\"Duder\"),\n lastName: Observable(\"Man\")\n };\n model.name = Observable(function() {\n return \"\" + (this.firstName()) + \" \" + (this.lastName());\n }, model);\n model.name.observe(function(newValue) {\n assert.equal(newValue, \"Duder Bro\");\n return done();\n });\n return model.lastName(\"Bro\");\n });\n});\n" }, "lib/test/pkg/index": { "content": "var ModLoader, Require, compile, crudeRequire, exec, htmlForPackage, jsForPackage, minify, registerCompiler, testPkg, _ref;\n\n_ref = require(\"/lib/pkg/index\"), compile = _ref.compile, crudeRequire = _ref.crudeRequire, exec = _ref.exec, htmlForPackage = _ref.htmlForPackage, jsForPackage = _ref.jsForPackage, minify = _ref.minify, ModLoader = _ref.ModLoader, registerCompiler = _ref.registerCompiler, Require = _ref.Require;\n\nmocha.setup({\n globals: ['CoffeeScript', 'marked', 'stylus', 'UglifyJS']\n});\n\ntestPkg = {\n distribution: {\n main: {\n content: \"alert('heyy');\"\n }\n },\n dependencies: {\n \"!system\": PACKAGE\n },\n config: {\n description: \"Yolo\"\n }\n};\n\ndescribe(\"pkg\", function() {\n describe(\"htmlForPackage\", function() {\n return it(\"should blob up html\", function() {\n var blob;\n blob = htmlForPackage(testPkg);\n return assert(blob instanceof Blob);\n });\n });\n describe(\"compile\", function() {\n it(\"should compile CoffeeScript by lazy loading compiler\", function() {\n return compile(\"hello.coffee\", \"alert 'hello'\");\n });\n it(\"should 'compile' JavaScript\", function() {\n var src;\n src = \"alert('hello');\";\n return compile(\"hello.js\", src).then(function(program) {\n return assert.equal(program, src);\n });\n });\n it(\"should compile markdown after lazy loading compiler\", function() {\n return compile(\"TODO.md\", \"- [x] Lazy Load compilers\");\n });\n it(\"should compile stylus after lazy loading compiler\", function() {\n return compile(\"yo.styl\", \"body\\n background-color: green\");\n });\n it(\"should fail if no known compiler\", function() {\n return compile(\"wat.doot\", \"\")[\"catch\"](function(e) {\n return assert.equal(e.message, \"Couldn't compile 'wat.doot'. No compiler for '.doot'\");\n });\n });\n return it(\"should register compilers\", function() {\n registerCompiler(\"doot\", function() {\n return \"doot\";\n });\n return compile(\"wat.doot\", \"\");\n });\n });\n describe(\"exec\", function() {\n it(\"should bind this\", function() {\n var x, y;\n x = {};\n y = exec(\"return this\", null, x);\n return assert.equal(x, y);\n });\n it(\"env is optional\", function() {\n var r;\n r = exec(\"return 5\");\n return assert.equal(r, 5);\n });\n return it(\"should bind env values\", function() {\n var r;\n r = exec(\"return a + this\", {\n a: 2\n }, 3);\n return assert.equal(r, 5);\n });\n });\n describe(\"crudeRequire\", function() {\n return it(\"should return what is exported\", function() {\n var result;\n result = crudeRequire(\"module.exports = \\\"cool\\\";\");\n return assert.equal(result, \"cool\");\n });\n });\n describe(\"Require\", function() {\n return it(\"should be exported\", function() {\n return assert(Require);\n });\n });\n describe(\"jsForPackge\", function() {\n return it(\"should generate a string of js\", function() {\n var js;\n js = jsForPackage(PACKAGE);\n return assert(typeof js === 'string');\n });\n });\n describe.skip(\"minify\", function() {\n return it(\"makes packages smaller\", function() {\n return minify(PACKAGE, console).then(console.log);\n });\n });\n return describe(\"Mod Loader\", function() {\n return it(\"should load remote modules\", function() {\n return ModLoader;\n });\n });\n});\n" }, "lib/test/pkg/require": { "content": "var latestRequire, requireSrc, sampleDir;\n\nrequireSrc = \"/lib/pkg/require\";\n\nlatestRequire = require(requireSrc).generateFor(PACKAGE);\n\nsampleDir = \"/data/pkg/samples/\";\n\ndescribe(\"require\", function() {\n it(\"should not exist globally\", function() {\n return assert(!global.require);\n });\n it(\"should be able to require a file that exists with a relative path\", function() {\n return assert(latestRequire(\"\" + sampleDir + \"terminal\"));\n });\n it(\"should get whatever the file exports\", function() {\n return assert(latestRequire(\"\" + sampleDir + \"terminal\").something);\n });\n it(\"should not get something the file doesn't export\", function() {\n return assert(!latestRequire(\"\" + sampleDir + \"terminal\").something2);\n });\n it(\"should throw a descriptive error when requring circular dependencies\", function() {\n return assert.throws(function() {\n return latestRequire(\"\" + sampleDir + \"circular\");\n }, /circular/i);\n });\n it(\"should throw a descriptive error when requiring a package that doesn't exist\", function() {\n return assert.throws(function() {\n return latestRequire(\"does_not_exist\");\n }, /not found/i);\n });\n it(\"should throw a descriptive error when requiring a relative path that doesn't exist\", function() {\n return assert.throws(function() {\n return latestRequire(\"/does_not_exist\");\n }, /Could not find file/i);\n });\n it(\"should recover gracefully enough from requiring files that throw errors\", function() {\n assert.throws(function() {\n return latestRequire(\"\" + sampleDir + \"throws\");\n });\n return assert.throws(function() {\n return latestRequire(\"\" + sampleDir + \"throws\");\n }, function(err) {\n return !/circular/i.test(err);\n });\n });\n it(\"should cache modules\", function() {\n var result;\n result = latestRequire(\"\" + sampleDir + \"random\");\n return assert.equal(latestRequire(\"\" + sampleDir + \"random\"), result);\n });\n it(\"should be able to require a JSON package object\", function() {\n var SAMPLE_PACKAGE, result;\n SAMPLE_PACKAGE = {\n entryPoint: \"main\",\n distribution: {\n main: {\n content: \"module.exports = require('./other')\"\n },\n other: {\n content: \"module.exports = 'TEST'\"\n }\n }\n };\n result = latestRequire(SAMPLE_PACKAGE);\n return assert.equal(\"TEST\", result);\n });\n return it(\"should be able to require something packaged with browserify\", function() {\n return assert.equal(latestRequire(\"\" + sampleDir + \"browserified\"), \"coolio\");\n });\n});\n\ndescribe(\"package wrapper\", function() {\n it(\"should be able to generate a package wrapper recursively\", function() {\n var pkgString;\n pkgString = latestRequire.packageWrapper(PACKAGE, \"window.r = require\");\n Function(pkgString)();\n Function(r.packageWrapper(PACKAGE, \"window.r2 = require\"))();\n Function(r2.packageWrapper(PACKAGE, \"window.r3 = require\"))();\n assert(r2);\n assert(r3);\n delete r;\n delete r2;\n return delete r3;\n });\n return it(\"should be able to execute code in the package context\", function() {\n var code;\n code = latestRequire.packageWrapper(PACKAGE, \"window.test = require.packageWrapper(PACKAGE, 'alert(\\\"heyy\\\")');\");\n Function(code)();\n assert(window.test);\n return delete window.test;\n });\n});\n\ndescribe(\"public API\", function() {\n mocha.setup({\n globals: ['system', 'OBSERVABLE_ROOT_HACK']\n });\n return it(\"should be able to require a JSON package directly\", function() {\n assert(require(requireSrc).loadPackage({\n distribution: {\n main: {\n content: \"global.test2 = true\"\n }\n }\n }));\n assert(window.test2);\n return delete window.test2;\n });\n});\n\ndescribe(\"module context\", function() {\n it(\"should know __dirname\", function() {\n return assert.equal(\"lib/test/pkg\", __dirname);\n });\n it(\"should know __filename\", function() {\n return assert(__filename);\n });\n return it(\"should know its package\", function() {\n return assert(PACKAGE);\n });\n});\n\ndescribe(\"malformed package\", function() {\n var malformedPackage;\n malformedPackage = {\n distribution: {\n yolo: \"No content!\"\n }\n };\n return it(\"should throw an error when attempting to require a malformed file in a package distribution\", function() {\n var r;\n r = require(requireSrc).generateFor(malformedPackage);\n return assert.throws(function() {\n return r.require(\"yolo\");\n }, function(err) {\n return !/malformed/i.test(err);\n });\n });\n});\n\ndescribe(\"dependent packages\", function() {\n it(\"should allow for arbitrary characters\", function() {\n var r;\n r = require(requireSrc).generateFor({\n dependencies: {\n \"#$!jadelet\": {\n entryPoint: \"main\",\n distribution: {\n main: {\n content: \"module.exports = 'ok';\"\n }\n }\n }\n }\n });\n return assert.equal(r(\"#$!jadelet\"), \"ok\");\n });\n PACKAGE.dependencies[\"test-package\"] = {\n distribution: {\n main: {\n content: \"module.exports = PACKAGE.name\"\n }\n }\n };\n PACKAGE.dependencies[\"strange/name\"] = {\n distribution: {\n main: {\n content: \"\"\n }\n }\n };\n it(\"should raise an error when requiring a package that doesn't exist\", function() {\n return assert.throws(function() {\n return latestRequire(\"nonexistent\");\n }, function(err) {\n return /nonexistent/i.test(err);\n });\n });\n it(\"should be able to require a package that exists\", function() {\n return assert(latestRequire(\"test-package\"));\n });\n it(\"Dependent packages should know their names when required\", function() {\n return assert.equal(latestRequire(\"test-package\"), \"test-package\");\n });\n return it(\"should be able to require by pretty much any name\", function() {\n return assert(latestRequire(\"strange/name\"));\n });\n});\n" }, "lib/test/polyfill": { "content": "var endsWith, startsWith, _ref;\n\n_ref = require(\"../polyfill\"), startsWith = _ref.startsWith, endsWith = _ref.endsWith;\n\ndescribe(\"Polyfill\", function() {\n return describe(\"String\", function() {\n it(\"startsWith\", function() {\n assert(\"\".startsWith);\n assert.equal(startsWith.call(\"abcd\", \"ab\"), true);\n assert.equal(startsWith.call(\"abcd\", \"ab\", -1), true);\n assert.equal(startsWith.call(\"abcd\", \"ab\", 0), true);\n assert.equal(startsWith.call(\"abcd\", \"ab\", 1), false);\n return assert.equal(startsWith.call(\"abcd\", \"b\", 1), true);\n });\n return it(\"endsWith\", function() {\n assert(\"\".endsWith);\n assert.equal(endsWith.call(\"abcd\", \"cd\"), true);\n return assert.equal(endsWith.call(\"raddad\", \"rad\", 3), true);\n });\n });\n});\n" }, "lib/test/postmaster": { "content": "var Postmaster, dataUrl, htmlContent, randId, scriptContent, srcUrl, testFrame;\n\nPostmaster = require(\"/lib/postmaster\");\n\nrandId = function() {\n return Math.random().toString(36).substr(2);\n};\n\nscriptContent = function() {\n var fn;\n fn = function() {\n var pm;\n return pm = Postmaster({\n delegate: {\n echo: function(value) {\n return value;\n },\n throws: function() {\n throw new Error(\"This always throws\");\n },\n promiseFail: function() {\n return Promise.reject(new Error(\"This is a failed promise\"));\n },\n send: function() {\n return pm.send.apply(pm, arguments);\n }\n }\n });\n };\n return \"(function() {\\nvar module = {};\\n(function() {\\n\" + PACKAGE.distribution[\"lib/postmaster\"].content + \";\\n})();\\nvar Postmaster = module.exports;\\n(\" + (fn.toString()) + \")();\\n})();\";\n};\n\nhtmlContent = function() {\n return \"\\n

\\n