├── .gitignore ├── LICENSE ├── README.md ├── bin └── coat.js ├── bower.json ├── build ├── coat.js ├── coat.min.js └── coat.min.map ├── lib └── index.js ├── package.json ├── src ├── controllers.js ├── index.js ├── model.js ├── modules.js ├── router.js ├── util.js └── views.js └── test ├── test.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | .DS_Store 4 | Makefile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Daily Muse Inc 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About Mithril Coat 2 | Mithril Coat is a minimalistic frontend web framework that builds on [Mithril](https://github.com/lhorie/Mithril). Mithril Coat provides two tools that are meant to be used in conjunction with each other. 3 | 4 | * A lightweight library around Mithril that provides code structure and a flux like architecture using [pubsub-js](https://github.com/mroderick/PubSubJS). 5 | * A templating language that uses HTML tags that compiles to Mithril templates. 6 | 7 | Mithril Coat requires jQuery and is expected to be used in conjunction with Browserify (we provide a browserify plugin, [Coatify](https://github.com/dailymuse/coatify) to require Mithril Coat templates via Browserify). 8 | 9 | NOTE: the documentation assumes a familiarity with Mithril and terminology surrounding Mithril. 10 | 11 | ## Install 12 | To install the front-end package via bower `bower install Mithril-coat`. 13 | 14 | To install the template compiler `npm install Mithril-coat -g`. 15 | 16 | ## Mithril Coat 17 | Mithril Coat is composed of three primary objects (View, Controllers, and Models) as well as some utility functions. 18 | Views use Model properties to correctly display the correct html through a Mithril Coat template. Views also listen for dom events and using Mithril Coat's global pubsub system, publish that a dom interaction has occcured. Controllers can listen to specific events that the views publish and determine whether a model should be manipulated and whether an autoredraw should occur. This allows Mithril Coat to have a flux like architecture and allows for Mithril Coat to have "stateless" controllers (since controllers just need to know they are manipulating a model, but not which specific model). 19 | 20 | Mithril Coat also provides some utility functions for routing and initializing Mithril components. 21 | 22 | Here are a few notions around Mithril Coat: 23 | * Views should never manipulate Models 24 | * Models should only be manipulated by Controllers 25 | * Views should not call methods on Controllers and vice versa 26 | * Views and Controllers should interact via Mithril Coat's pubsub system 27 | * Mithril autoredraw function should only be called from a Controller 28 | 29 | NOTE: Views, Controller, and Models all follow prototypical inheritance. They all accept an object when initialized. All key, values of that object will be bound as properties of that Object. 30 | 31 | ## Views 32 | Mithril Coat Views are very similar to [Backbone](http://backbonejs.org/) and provide a very simple interface to work with dom events. Mithril Coat has a notion of 2 different types of views: 33 | 34 | 1. Base Views - coat.View 35 | 2. Templated Views - coat.TemplatedView 36 | 37 | ### new coat.View({}) 38 | Base Views provide a number of convenience methods for interacting with existing dom nodes. 39 | 40 | All views expect to be initialized with a `$el` key and value. 41 | 42 | ```javascript 43 | var view = new coat.View({ 44 | $el: $("body") 45 | }) 46 | ``` 47 | 48 | #### coat.View.prototype.domEvents() 49 | Dom events takes a similar approach to Backbone dom events and serves as a way to have event delegation on the view's $el. 50 | 51 | domEvents returns a mapping of `"[events] [selector]": "functionOnView"`. 52 | 53 | ```javascript 54 | view.prototype.domEvents = function() { 55 | return { 56 | "click a": "onClickLink" 57 | } 58 | } 59 | 60 | view.prototype.onClickLink = function(e) { 61 | console.log('linked clicked') 62 | } 63 | ``` 64 | 65 | #### coat.View.prototype.$(jquerySelector) 66 | returns a jQuery selector that match the selector inside the $el. 67 | 68 | ### new coat.TemplatedView({}) 69 | Extends coat.View and adds additional functionality for views that use Mithril templates. 70 | 71 | All `coat.TemplatedView` expect a `template` property on instantiation. This will be the template that the view renders. 72 | 73 | If you want to pass model data to a view it should be done via the `state` property which is exposed to the Mithril Coat template. 74 | 75 | Some things to note about Mithril Coat templated views: 76 | * each Mithril Coat template is wrapped in a div generated by Mithril Coat. 77 | * Mithril Coat ensures that events are cleaned up on each redraw. 78 | * Mithril Coat templates have a `view` tag to generate subviews, templated views clean up their subviews and all the events for these subviews. 79 | * for views that are part of an SPA it is not necessary to pass in an `$el`, however for views that use `coat.initModule()` to initialize a Mithril Coat component it's necessary to pass an `$el` to the view object. 80 | 81 | ```javascript 82 | var sampleTemplate = require("./template.coat") 83 | var templatedView = new coat.TemplatedView({ 84 | $el: $("body"), 85 | template: sampleTemplate, 86 | state: new coat.Model({ 87 | name: "daily muse", 88 | version: "1.0.1" 89 | }) 90 | }) 91 | ``` 92 | 93 | #### coat.TemplatedView.prototype.render() 94 | Should be called to render the Mithril template. 95 | 96 | #### coat.TemplatedView.prototype.config(element, isInit, context) 97 | The config method is called whenever the current template is rendered to the page. 98 | 99 | NOTE: these arguments are the same as Mithril passes to the [config attribute](https://lhorie.github.io/Mithril/Mithril.html#the-config-attribute). 100 | 101 | #### coat.TemplatedView.prototype.onunload() 102 | The current templated view was unloaded. Can be used if you need to call methods on a third party library when a view is unloaded. 103 | 104 | ## Controllers 105 | Mithril Coat controllers are meant to be used to manipulate Models and to initiate Mithril redraws (either via Model requests or via autoredraws). 106 | 107 | ### new coat.Controller({}) 108 | Controllers do not take any default arguments. 109 | 110 | ```javascript 111 | var controller = new coat.Controller({ 112 | model: new coat.Model({ 113 | url: "api/confirm" 114 | }) 115 | }) 116 | ``` 117 | 118 | #### coat.Controller.prototype.events() 119 | Events are bound to a module using Pubsub-js, which is a global pubsub system and therefore events are not Module specific. Modules expect the events to return an object mapping of event names to functions to call when the event is published. 120 | 121 | Controller events can be published via `coat.publish('some-event-name')` or `coat.publishSync('some-event-name')`. To see the differences between `publish` and `publishSync` please checkout pubsub-js. Views should typically be the ones that publish controller events, although there are exceptions (like when a controller wants to interact with another controller, or in a components controller function). 122 | 123 | ```javascript 124 | controller.prototype.events = function() { 125 | return { 126 | "on-button-clicked": this.buttonClicked 127 | } 128 | } 129 | 130 | controller.prototype.buttonClicked = function() { 131 | console.log('called when a view publishes an event via coat.publish("on-button-clicked")'); 132 | } 133 | ``` 134 | 135 | #### coat.Controller.prototype.autoredraw(cb, opts) 136 | Redraws a view using Mithril's `startComputation()` and `endComputation` in a try, finally block as recommended by Mithril. Calls the callback and passes opts as an argument to the callback. 137 | 138 | ```javascript 139 | // inside some function in a controller 140 | 141 | controller.prototype.buttonClicked = function() { 142 | this.autoredraw(function(opts) { 143 | console.log(opts); 144 | }, opts); 145 | } 146 | ``` 147 | 148 | ## Models 149 | Mithril Coat models provide convenience methods for interacting with Mithril models. And dealing with state thorughout your application. 150 | 151 | ### new coat.Model({}) 152 | All keys and values that are passed in the opts object are set as properties on the Model as Mithril props respectively. 153 | 154 | ```javascript 155 | var model = new coat.Model({ 156 | name: "Mithril-coat", 157 | version: 1.0, 158 | }); 159 | 160 | console.log(model.name()) // prints "Mithril-coat" to the console 161 | ``` 162 | 163 | There are also some keys that are available on every model: 164 | * `this.modelKeys` is a list of all keys that are in the object passed into the constructor. All json keys that is returned from model requests are also added to the modelKeys list. 165 | * `this.loading()` a boolean Mithril prop that indicates that the model is currently being requested. Mithril Coat automatically sets this the value of this property. It can be used to show a loading spinner in a templated view. 166 | * `this.requestError()` a boolean Mithril prop that indicates whether there was a request error 167 | 168 | ### model.setProps({}) 169 | Method to set properties on the model. Accepts a key value object that are set as Mithril props on the model. 170 | 171 | ```javascript 172 | model.setProps({ 173 | updatedVersion: 2.0 174 | }); 175 | 176 | console.log(model.updatedVersion()) // prints 2.0 177 | ``` 178 | 179 | ### model.getProps() 180 | Returns all the Mithril properties on the model as an object mapping key to value. 181 | 182 | ### model.url || model.url() 183 | the url can be set as a property on the Model oras a function. It should return a url string to request 184 | 185 | ```javascript 186 | model.prototype.url = function() { 187 | return "/api/Mithril-coat/" + this.version(); 188 | } 189 | ``` 190 | 191 | ### model.xhrConfig(xhr) 192 | This function should be extended if it is necessary to configure the xhr request. 193 | 194 | ```javascript 195 | model.prototype.xhrConfig = function(xhr) { 196 | // in order to avoid caching issues in ie10 197 | xhr.setRequestHeader("Content-Type", "application/json"); 198 | } 199 | ``` 200 | 201 | ### Model Requests 202 | In addition to allowing you to set the underlying xhr requests - Mithril Coat provides a few other convenience methods. All method calls accept an object of key values. Possible keys that Mithril Coat will place in the options m.request include: 203 | 204 | * "user" 205 | * "password" 206 | * "data" - data to be submitted (expected to be an object of key value mappings) 207 | * "background" 208 | * "initialValue" 209 | * "unwrapSuccess" 210 | * "unwrapError" 211 | * "serialize" 212 | * "extract" 213 | * "type" 214 | 215 | Mithril Coat also allows you to pass in `success` and `error` keys that are mapping to function callbacks. Each callback receives two arguments `success: function(response, model)` where the response is the response from the server and the model is the current requested model. 216 | 217 | #### model.get({}}) 218 | Submits a get request and optionally specifies the method to use in opts 219 | 220 | ``` javascript 221 | model.get({ 222 | success: function(response, model) { 223 | console.log(response) 224 | }, 225 | error: function(error, model) {` 226 | console.log(error) 227 | } 228 | }) 229 | ``` 230 | 231 | #### model.save({}) 232 | By default if a `"method"` key is not set in opts and an id is set on the model then Mithril Coat will set the method request to `"PUT"` and if there is not id it will set the method request to `"POST"`. However there are times when you should submit a `"PUT"` request even though an id exists - therefore we provide the flexibility to determine which method to use. 233 | 234 | #### model.delete({}) 235 | Submits a delete request to the server. 236 | 237 | ## Routing and Modules 238 | Mithril Coat provides some nice utility methods that wrap around Mithril's routing and components. 239 | 240 | ### coat.setRoutes($rootEl, routes) 241 | Sets the Mithril Coat routes for the single page application. Mithril Coat sets Mithril to route on the pathname. 242 | 243 | `$rooEl` is a jQuery object in which the the Mithril templates will be rendered. `routes` should be an object mapping route names to Mithril a component (`{controller: function() {}, view: () -> console.log('render some view here via view.render()')}`). 244 | 245 | ```javascript 246 | template = require("./some_template.coat") 247 | 248 | view = new coat.TemplatedView({ 249 | template: template, 250 | state: new coat.Model({ 251 | name: "The Muse" 252 | }) 253 | }); 254 | 255 | coat.setRoutes($("body"), { 256 | "/": { 257 | controller: function() { 258 | console.log('take some actions that need to be taken on each route update') 259 | }, view: function() { 260 | view.render(); 261 | } 262 | } 263 | }) 264 | ``` 265 | 266 | ### coat.getParams() 267 | Gets the current query parameters in the url. This function can only be called after `coat.setRoutes` has been called. Returns a mapping of query parameter keys to their respective values. 268 | 269 | ### coat.updateRoute(route, params, shouldReplaceHistory) 270 | Routes to the new route and the query parameters listed. 271 | 272 | `route` should be a string representing the new path. `params` should be an object mapping of query parameter keys to values. `shouldReplaceHistory` is a boolean whether the history should be replaced. 273 | 274 | ### coat.updateParams(params, shouldReplaceHistory) 275 | Is a convenience method that allows you to update a select group of query parameters. Sometimes you may have 10+ query parameters, but you only are routing to update a value or two. Furthermore you may not want to have to deal with the existing params object. This is just a convenience method that can be used to update select query parameters. 276 | 277 | `params` should be an object mapping of query parameter keys to values that you want to update. `shouldReplaceHistory` is a boolean whether the history should be replaced. 278 | 279 | ### coat.initModule(view, controllerCb) 280 | A convenient wrapper to initialize a mithirl component. 281 | 282 | ``` javascript 283 | model = new coat.Model({ 284 | url: "api/some-request" 285 | }) 286 | 287 | view = new coat.TemplatedView({ 288 | $el: $("body"), 289 | state: model 290 | }) 291 | 292 | /* 293 | @param [coat.TemplatedView] view a Mithril Coat view that's used to mount the 294 | Mithril component 295 | @param [Function] controllerCb a controller callback that's called in the 296 | Mithril component controller 297 | */ 298 | coat.initModule(view, function() { 299 | console.log('some action to take before the view is rendered') 300 | model.get({}) 301 | }); 302 | ``` 303 | 304 | ## Mithril Coat Templates 305 | Mithril Coat templates are Mithril templates that are written in HTML. The files are saved with a .coat extension and are then compiled to JavaScript by the Mithril Coat compiler. 306 | 307 | Mithril Coat templates are simply html templates that are compiled to Mithril. The templating language is expected to be used in conjunction with [Browserify](http://browserify.org/) as it `module exports` all the templates. You can use the [Coatify](https://github.com/dailymuse/coatify) plug-in to `require` Mithril Coat Templates. 308 | 309 | All Mithril Coat templates receive two variables: `(view, state)`. `view` is a reference to the view object that has the template so any property on the view is available to you via the view object. `state` is the state that was passed in the view on initialization. `state` is the place where any model or ui data should exist. 310 | 311 | Mithril Coat template support all html tags and have an additional 7 html tags 312 | ``` 313 | * if, elif, else 314 | * val 315 | * raw 316 | * map 317 | * nbsp 318 | * template 319 | * view 320 | ``` 321 | 322 | #### `, ` 323 | Every if and elif tag need to have an `expr` attribute on it and the value of `expr` should be a JavaScript expression. All if, elif, and else tags need to be closed. All tags must also have valid content placed in between their tags. 324 | 325 | ``` html 326 | 327 | 328 |

The Window objext exist

329 |
330 | 331 |

Hello World

332 |
333 | 334 |

In the else

335 |
336 | 337 | 343 | ``` 344 | 345 | #### `` 346 | `val` is a self closing tag that evaluates a JavaScript. The only attribute it accepts is `expr`. 347 | 348 | ``` html 349 | 353 |

354 | 355 |

356 | ``` 357 | 358 | #### `` 359 | `raw` wraps a string or a JavaScript expression in `Mithril.trust`. The two different attributes are `val` and `expr`. 360 | 361 | `val` accepts a JavaScript expression, so if `state` has a property `message: "hello world The Muse", `val` should be used to display `state.message()`. 362 | 363 | `expr` should be used to display a string such as html character entities. 364 | 365 | ``` html 366 | 367 |

368 | 369 | 370 |

371 | 372 | 373 | 374 | ``` 375 | 376 | #### `` 377 | Allows for iterating over an object or an array. 378 | 379 | If you are iterating over an object `key` is the current key in the iteration and `val` is the current value of that key. 380 | 381 | If you are iterating over an array `key` is the current index and `val` is the current value of that index. 382 | 383 | ``` html 384 | 385 | 386 |

Value is: at index:

387 |
388 | 389 | 390 |

The Properties of The Muse are:

391 | 392 |

:

393 |
394 | ``` 395 | 396 | #### `` 397 | Add the `&nspb;` character the number of times specified in the `count` property. 398 | 399 | ``` html 400 | 401 | 402 | ``` 403 | 404 | #### `