/
index.html
1550 lines (1426 loc) · 75.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Epitome - MVC/MVP Framework for MooTools</title>
<link href="css/doctor.css" type="text/css" rel="stylesheet">
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1199722-3']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<a id="github-ribbon" href="https://github.com/epitome-mvc/Epitome"><img alt="Fork me on GitHub" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>
<div id="nav">
<div class="separator"></div>
<a title="Epitome - MVC/MVP Framework for MooTools" class="brand" href="#" style="background-image:url(images/epitome-logo-small.png)"></a>
<div class="separator"></div>
<div class="twitter extra">
<iframe allowtransparency="true" frameborder="0" scrolling="no" style="width: 162px; height: 20px;" src="https://platform.twitter.com/widgets/follow_button.html?screen_name=D_mitar&show_count=false"></iframe>
</div>
<div class="separator"></div>
<ul id="sections"><li class="l2"><a href="#changelog">Changelog</a></li><li class="l2"><a href="#epitomeevents">Epitome.Events</a><ul><li class="l3"><a href="#epitomeevents/on">on</a></li><li class="l3"><a href="#epitomeevents/off">off</a></li><li class="l3"><a href="#epitomeevents/trigger">trigger</a></li><li class="l3"><a href="#epitomeevents/listento">listenTo</a></li><li class="l3"><a href="#epitomeevents/stoplistening">stopListening</a></li></ul></li><li class="l2"><a href="#epitomemodel">Epitome.Model</a><ul><li class="l3"><a href="#epitomemodel/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#epitomemodel/set">set</a></li><li class="l3"><a href="#epitomemodel/get">get</a></li><li class="l3"><a href="#epitomemodel/tojson">toJSON</a></li><li class="l3"><a href="#epitomemodel/unset">unset</a></li><li class="l3"><a href="#epitomemodel/empty">empty</a></li><li class="l3"><a href="#epitomemodel/destroy">destroy</a></li><li class="l3"><a href="#epitomemodel/model-properties">Model properties*</a></li><li class="l3"><a href="#epitomemodel/model-validators">Model validators*</a></li></ul></li><li class="l2"><a href="#epitomemodelsync">Epitome.Model.Sync</a><ul><li class="l3"><a href="#epitomemodelsync/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#epitomemodelsync/sync">sync</a></li><li class="l3"><a href="#epitomemodelsync/postprocessor">postProcessor</a></li><li class="l3"><a href="#epitomemodelsync/save">save</a></li><li class="l3"><a href="#epitomemodelsync/preprocessor">preProcessor</a></li><li class="l3"><a href="#epitomemodelsync/fetch">fetch</a></li></ul></li><li class="l2"><a href="#epitomecollection">Epitome.Collection</a><ul><li class="l3"><a href="#epitomecollection/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#epitomecollection/reset">reset</a></li><li class="l3"><a href="#epitomecollection/addmodel">addModel</a></li><li class="l3"><a href="#epitomecollection/removemodel">removeModel</a></li><li class="l3"><a href="#epitomecollection/getmodel">getModel</a></li><li class="l3"><a href="#epitomecollection/getmodelbycid">getModelByCID</a></li><li class="l3"><a href="#epitomecollection/getmodelbyid">getModelById</a></li><li class="l3"><a href="#epitomecollection/tojson">toJSON</a></li><li class="l3"><a href="#epitomecollection/empty">empty</a></li><li class="l3"><a href="#epitomecollection/sort">sort</a></li><li class="l3"><a href="#epitomecollection/reverse">reverse</a></li><li class="l3"><a href="#epitomecollection/find">find</a></li><li class="l3"><a href="#epitomecollection/findone">findOne</a></li><li class="l3"><a href="#epitomecollection/array-helpers">Array helpers</a></li><li class="l3"><a href="#epitomecollection/collection-properties">Collection properties*</a></li></ul></li><li class="l2"><a href="#epitomecollectionsync">Epitome.Collection.Sync</a><ul><li class="l3"><a href="#epitomecollectionsync/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#epitomecollectionsync/fetch">fetch</a></li><li class="l3"><a href="#epitomecollectionsync/postprocessor">postProcessor</a></li></ul></li><li class="l2"><a href="#epitomeview">Epitome.View</a><ul><li class="l3"><a href="#epitomeview/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#epitomeview/render">render</a></li><li class="l3"><a href="#epitomeview/setelement">setElement</a></li><li class="l3"><a href="#epitomeview/template">template</a></li><li class="l3"><a href="#epitomeview/empty">empty</a></li><li class="l3"><a href="#epitomeview/dispose">dispose</a></li><li class="l3"><a href="#epitomeview/destroy">destroy</a></li></ul></li><li class="l2"><a href="#epitomestorage">Epitome.Storage</a><ul><li class="l3"><a href="#epitomestorage/store">store</a></li><li class="l3"><a href="#epitomestorage/retrieve">retrieve</a></li><li class="l3"><a href="#epitomestorage/eliminate">eliminate</a></li></ul></li><li class="l2"><a href="#epitometemplate">Epitome.Template</a></li><li class="l2"><a href="#eptiomerouter">Eptiome.Router</a><ul><li class="l3"><a href="#eptiomerouter/constructor-initialize">constructor (initialize)</a></li><li class="l3"><a href="#eptiomerouter/addroute">addRoute</a></li><li class="l3"><a href="#eptiomerouter/removeroute">removeRoute</a></li></ul></li><li class="l2"><a href="#examples">Examples</a><ul><li class="l3"><a href="#examples/sync-and-storage">Sync and Storage</a></li><li class="l3"><a href="#examples/prototyping-views">Prototyping Views</a></li><li class="l3"><a href="#examples/todomvc-reference">TodoMVC reference</a></li></ul></li><li class="l2"><a href="#download-building">Download + Building</a><ul><li class="l3"><a href="#download-building/amd-builder">AMD Builder</a></li><li class="l3"><a href="#download-building/commonjs">CommonJS</a></li></ul></li><li class="l2"><a href="#testing">Testing</a></li><li class="l2"><a href="#development-and-contribution">Development and contribution</a></li><li class="l2"><a href="#credits-and-licensing">Credits and licensing</a></li><li class="l2"><a href="#comments">Comments</a></li></ul>
<div class="separator"></div>
<div class="extra" id="github">
<a href="https://github.com/epitome-mvc/Epitome">Source on Github</a>
</div>
<div class="extra" id="github-issues">
<a href="https://github.com/epitome-mvc/Epitome/issues">Issues</a>
</div>
<div class="separator"></div>
<div class="extra" id="github">
<a href="http://travis-ci.org/epitome-mvc/Epitome" class="travis">
<img src="http://travis-ci.org/epitome-mvc/Epitome.png?branch=master"></a>
</div>
<div class="extra generated">
Generated by <a href="https://github.com/DimitarChristoff/doctor" target="_blank" title="generate documentation from markdown">Doctor, MD</a>
</div>
</div>
<div id="content" class="container">
<p><img src="https://github.com/DimitarChristoff/Epitome/raw/master/example/epitome-logo.png" alt="Epitome"></p>
<blockquote>
<p><em>Epitome: a typical example of a characteristic or class; embodiment; personification; model</em></p>
</blockquote>
<p>Epitome is a new extensible and modular open-source MVC* framework, built out of MooTools Classes and Events. See <a href="#credits-and-licensing">credits and licensing</a></p>
<p><a href="http://travis-ci.org/DimitarChristoff/Epitome"><img src="https://secure.travis-ci.org/DimitarChristoff/Epitome.png?branch=master" alt="Build Status"></a></p>
<p><strong>BUT, IS IT REALLY MVC?</strong></p>
<blockquote>
<p><jiggliemon> MVD, Model View Don'task</p>
</blockquote>
<p>Strictly speaking, <code>Epitome.View</code> is closer to a <em>presenter</em> in a MVP implementation than a classic MVC one, with thin controller logic around the views. However, because <code>Epitome.View</code> is also very unassuming, it can be used in a more classical MVC pattern context for multiple purposes.</p>
<p>If you feel strongly about semantics of the patterns used, you should look at <a href="http://addyosmani.com/blog/digesting-javascript-mvc-pattern-abuse-or-evolution/">Digesting JavaScript MVC – Pattern Abuse Or Evolution?</a> by Addy Osmani, a talk he gave at London Ajax recently.</p>
<p>Epitome's API is still subject to small changes and improvements (mostly additions of events and bug fixes), which means documentation is not overly verbose. The non-minified code has a lot of inline comments to ease understanding and development.</p>
<p>Current version: <strong>0.6.1</strong></p>
<p><a class="btn btn-large btn-primary" href="#download-building">Epitome Builder</a>
<a class="btn btn-large" href="https://epitomemvp.uservoice.com/" target="_blank">Issue / Discussion on UserVoice</a></p>
<p>A quick-and-dirty way to add the whole minified library, courtesy of cdnjs.com:</p>
<pre class="prettyprint linenums"><code class="lang-HTML"><script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/epitome/0.3.0/Epitome-min.js"></script></code></pre>
<h2 id="changelog">Changelog</h2>
<ul>
<li>0.6.0<ul>
<li>Breaking: custom setters need to return a value only, not re-implement <code>_set</code> (see #14)</li>
</ul>
</li>
<li>0.6.0<ul>
<li>Breaking: enabled <code>Slick.parser</code> under <code>node.js</code> via <a href="http://npmjs/slicker/">slicker</a>. Collection.find methods now work
just like they do in the browser.</li>
</ul>
</li>
<li>0.5.0<ul>
<li>BUGFIX: Collection needs <code>.length === 0</code> when instantiated w/o any models</li>
<li><code>Collection.reset</code> removes models first</li>
</ul>
</li>
<li>0.4.1<ul>
<li>Updated <code>bower.json</code> and added a <code>.npmignore</code> to stop distributing docs and tests as component install</li>
</ul>
</li>
<li>0.4.0<ul>
<li>Added CommonJS suport to Model, Collection, Template, Events and isEqual, reliant on <code>mootools npm</code></li>
</ul>
</li>
<li>0.3.1<ul>
<li>Added <code>.jshintrc</code> and changed all files to pass linting settings</li>
</ul>
</li>
<li>0.3.0<ul>
<li>Breaking API change, all events are now via <code>.on</code> / <code>.off</code> / <code>.trigger</code></li>
<li>Added <code>.listenTo(obj, event, cb)</code> and <code>.stopListening(obj, event, cb)</code> methods for easy pubsub and event bubbling</li>
</ul>
</li>
<li>0.2.2<ul>
<li>fixed a validators error firing errors without there being any.</li>
</ul>
</li>
<li>0.2.1<ul>
<li>the default Epitome Object now returns an instance of Events so you can mediate like <code>Epitome.trigger('awesome')</code>
between components. </li>
</ul>
</li>
<li>0.2.0<ul>
<li>big shift in he way Sync works with servers. Previously, Model and Collection were only accepting <code>application/json</code> as
content type to be returned. Failing to receive that caused the browser not to fire any readystatechange events, which in
turn caused all change/save/sync/error events not to bubble. As of this version, Epitome will also accept <code>text/plain</code> and
<code>text/html</code> as fallbacks. There is still an expectation to convert the responseText to an Object, so failure to do so will
fire <code>error</code> events instead.</li>
<li>Model.Sync now accept an optional argument <code>request</code>, which accepts a Class constructor for the MooTools Request with
your own definition of what success/failure is and conversion to JSON.</li>
</ul>
</li>
<li>0.1.10<ul>
<li>fixed bug where fetch/save/create/update events were firing before model.isNew was being changed</li>
</ul>
</li>
<li>0.1.9<ul>
<li>added <code>headers</code> to Model.Sync options that gets passed to the Request</li>
</ul>
</li>
<li>0.1.8<ul>
<li>added <code>queryParams</code> support to Collection.Sync::fetch, allows for pagination etc.</li>
</ul>
</li>
<li>0.1.7<ul>
<li>updated templating engine to a later version based on the one in _.js</li>
<li>added support for <code><%-var%></code> syntax to allow escaping of entities</li>
<li>Internally, extending the MooTools String proto and adding <code>escape</code> for dealing with entities in strings.</li>
<li>Updated tests</li>
</ul>
</li>
<li>0.1.6<ul>
<li>build.js server changes</li>
<li>moved to uglify2 for minification</li>
<li>updated Epitome-min.js to reflect</li>
<li>prevented stacking up for function deocration for Model instances' <code>.fireEvent</code></li>
<li>merged example tweak PR</li>
</ul>
</li>
<li>0.1.5<ul>
<li>small tweaks to AMD/browser wrappers, no breaking changes</li>
</ul>
</li>
<li>0.1.4<ul>
<li>breaking API change. deprecated: <code>model.sync.parse</code>, replaced with <code>preProcessor</code> and <code>postProcessor</code></li>
</ul>
</li>
</ul>
<p>All individual components of Epitome work as normal javscript files to be used in a browser as well as through <code>require.js</code> modules. See <a href="#download-building">Downloading + Building</a></p>
<h2 id="epitomeevents">Epitome.Events</h2>
<p>A lightweight replacement for the default MooTools Events class that adds shorthand for ease of use.</p>
<h3 id="epitomeevents/on">on</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(String) name, (Function) callback</code> or <code>(Object) name:callback</code> pairs</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Attaches an event or an event object on any Epitome instance (Model, Collection, View). You can also add multiple events
that will trigger the same callback function. Examples:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">// single
this.on('change', this.modelChange.bind(this));
// multiple
this.model.on('fetch change', this.render.bind(this));</code></pre>
<h3 id="epitomeevents/off">off</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(String) name</code>, optional <code>(Function) callback</code> or <code>(Object) name:callback</code> pairs</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Removes a specific event callback (if supplied) or all events sharing the same name (wildcard) when not.</p>
<h3 id="epitomeevents/trigger">trigger</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(String) name</code>, optional <code>(Array) arguments</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Fires an event on the current instance, passing any arguments passed (as Array or mixed).</p>
<h3 id="epitomeevents/listento">listenTo</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(Object) instance</code>, optional <code>(String) name</code>, optional <code>(Function) callback</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Subscribes to another instance's events. Can be broad or specific in how it attaches a listener. If no event name is
supplied, it will subscribe to ANY event fired by the other object instance. If no function callback is supplied, it will
bubble the event onto the host object instance but shift the callback arguments by 1, placing the other object instance
as the first argument. Use with caution as it can cause <code>event storms</code> if not much specificity has been set.</p>
<h3 id="epitomeevents/stoplistening">stopListening</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(Object) instance</code>, optional <code>(String) name</code>, optional <code>(Function) callback</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Un-subscribes your instance from another instance's Events. If no arguments supplied other than the other instance, it
will remove all subscriptions. You can optionally narrow it down by adding an event name or a name and a function in
particular you'd like to un-subscribe from.</p>
<h2 id="epitomemodel">Epitome.Model</h2>
<p>The Epitome.Model implementation at its core is a MooTools class with custom data accessors that fires events. As a MooTools Class, you can extend models or implement objects or other classes into your definitions. By default, the it comes with Epitome.Events and Options setters already (similar to the MooTools Class.Extras ones).</p>
<p>The Model can fire the following events:</p>
<ul>
<li><code>ready</code> - when instantiated</li>
<li><code>change</code> - when any properties have changed</li>
<li><code>change:key</code> - when a particular property <code>key</code> has changed</li>
<li><code>empty</code> - when a model has been emptied of all properties</li>
<li><code>destroy</code> - when a model has been <code>destroyed</code> and all data removed.</li>
</ul>
<p>The following methods are official API on all Model Classes:</p>
<h3 id="epitomemodel/constructor-initialize">constructor (initialize)</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) model</code>, <code>(Object) options</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>ready</code></em>
</p>
</div>
<p>The <code>model</code> - if passed, sets the internal data hash to a new derefrenced object. Special accessor properties, as defined in the <code>Epitome.Model.prototype.properties</code>, will run first and be applicable. See <a href="#epitomemodel/model-properties">properties</a> for more info.</p>
<p>The <code>options</code> object is a standard MooTools class options override and is being merged with the <code>Epitome.Model.prototype.options</code> when a new model is created. It typically contains various event handlers in the form of:</p>
<pre class="prettyprint linenums"><code class="lang-ace">var model = new Epitome.Model({}, {
defaults: {
userTitle: 'admin'
}
});
model.set('name', 'Bob');
console.log(model.toJSON());</code></pre>
<p>Supported: <code>(Object) options.defaults</code> - allows initial values of the model to be set if they are not being passed to the model constructor.</p>
<p>Of note, the Constructor fires an event called <code>ready</code> when done and setting the initial model does not fire a <code>change</code> event.</p>
<h3 id="epitomemodel/set">set</h3>
<hr>
<div class="alert" markdown="1">
<p>
<em>Expects arguments: mixed: <code>(String) key</code>, <code>(Mixed) value</code> - pair - or: <code>(Object) obj</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events:</em>
<ul>
<li> <code>change: function(changedProperties) {}</code></li>
<li> <code>change:key: function(valueForKey) {}</code></li>
<li> <code>error: function(objectFailedValidation) {}</code></li>
</ul>
</p>
</div>
<p>Allows changing of any individual model key or a set of key/value pairs, encapsulated in an object. Will fire a single <code>change</code> event with all the changed properties as well as a specific <code>change:key</code> event that passes just the value of the key as argument.</p>
<p>For typing of value, you can store anything at all (Primitives, Objects, Functions). Keep in mind that, when it comes to serialising the Model and sending it to the server, only Primitive types or ones with a sensible <code>toString()</code> implementation will make sense.</p>
<h3 id="epitomemodel/get">get</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments mixed: <code>(String) key</code> or <code>(Array) keys</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Returns known values within the model for either a single key or an array of keys. For an array of keys, it will return an object with <code>key</code> : <code>value</code> mapping.</p>
<h3 id="epitomemodel/tojson">toJSON</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
</div>
<p>Returns a de-referenced Object, containing all the known model keys and values.</p>
<h3 id="epitomemodel/unset">unset</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: mixed: <code>(String) key</code> or <code>(Array) keys</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>Removes keys from model, either a single one or an array of multiple keys. Does not fire a change event.</p>
<h3 id="epitomemodel/empty">empty</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>empty</code></em>
</p>
</div>
<p>Empties the model of all data and fires a single change event with all keys as well as individual <code>change:key</code> events.</p>
<h3 id="epitomemodel/destroy">destroy</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>destroy</code></em>
</p>
</div>
<p>Empties the model. No change event. Event is observed by Collections the model is a member of, where it triggers a <code>removeModel()</code></p>
<h3 id="epitomemodel/model-properties">Model properties*</h3>
<p>There are several additional properties each model instance will have.</p>
<h4>_attributes: {}</h4>
<hr>
<p>The attributes object is <strong>public</strong> (exposed to manipulation on the instance) and it holds the hash data for the model, based upon keys. It is de-referenced from the constructor object used when creating a model but should not be read directly (normally). Exported by <code>model.toJSON()</code>. Avoid changing this directly as it won't fire any change events at all.</p>
<h4>collections: []</h4>
<hr>
<p>An array that contains references to all instances of Epitome.Collection that the model is currently a member of. Useful for iteration as well as utilised by collections that want to know if Event observers are required.</p>
<h4>options: {}</h4>
<hr>
<p>A MooTools default options set, which can be on the prototype of the Model constructor.</p>
<h4>options.defaults: {}</h4>
<hr>
<p>An object with default Model Attributes to use when instantiating. Merged with Model object when creating.</p>
<h4>changedProperties: []</h4>
<hr>
<p>An array of all property keys that reflect the last <code>change</code> event. Available on all instances.</p>
<h4>properties: {}</h4>
<hr>
<p>A collection of custom accessors that override default <code>model.get</code> and <code>model.set</code> methods. For example:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">properties: {
foo: {
get: function() {
// scope is model
return this.foo();
},
set: function(value) {
// don't send this to the attributes, store in the instance directly.
// won't fire a traditional onChange, you need to manually fire the events
this.foo = value;
}
}
}</code></pre>
<p>In the example above, any calls to <code>model.set('foo', value)</code> and <code>model.get('foo')</code> are handled by custom functions. This is a pattern that allows you to use getters and setters for properties that are handled differently than normal ones. It can also be used as pre-processors for data. Make sure that you either set them on the instance directly or that you import the default ones for id in a custom prototype version as they are not merged like options.</p>
<p>If you want, you can also use custom setters as transformers. For instance, decorating a property <code>price</code> with a currency sign:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">properties: {
price: {
set: function(value) {
// failing to return a value here won't set anything.
return '$' + value; // will fire all the change events for you.
}
}
}</code></pre>
<h3 id="epitomemodel/model-validators">Model validators*</h3>
<p>You can also include basic validators into your model. Validators are an object on the Model prototype that maps any expected key to a function that will return <code>true</code> if the validation passes or a <code>string</code> error message or <code>false</code> on failure.</p>
<p>Here is an example:</p>
<pre class="prettyprint linenums"><code class="lang-ace">var validUser = new Class({
Extends: Epitome.Model,
validators: {
email: function(value) {
return (/(.+)@(.+){2,}\.(.+){2,}/).test(value) ? true : 'This looks like an invalid email address';
}
}
});
var userInstance = new validUser({}, {
onError: function(allErrors) {
console.log('The following fields were rejected', allErrors);
},
'onError:email': function(errorObj) {
// can have a custom message, action or whatever.
console.log('Email rejected', errorObj.error);
}
});
userInstance.set('email', 'this will fail!');</code></pre>
<p>The <code>error</code> event is observed by collections and views and fires on all view and collection instances.</p>
<h2 id="epitomemodelsync">Epitome.Model.Sync</h2>
<p>This is an example implementation of RESTful module that extends the base Epitome.Model class and adds the ability to read, update and delete models with remote server. In terms of implementation, there are subtle differences. The API and methods are as the normal <a href="#epitomemodel">Model</a>, unless outlined below:</p>
<h3 id="epitomemodelsync/constructor-initialize">constructor (initialize)</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) model</code>, <code>(Object) options</code></em>
</p>
</div>
<p>A model <code>id</code> with your model as well as setup a <code>urlRoot</code> either as a property of the model or as an options property is required for your model to be synced. The constructor function first calls the Epitome.Model constructor and then sets up the XHR instance and methods.</p>
<div class="alert">
<code>options.useJSON</code> (boolean) is an all-important way to control how your Model talks to your server backend. If your server is a native REST implementation, when this value is <code>true</code>, Epitome Sync will set the content-type to application/json and send a strigified JSON of your model on every POST or PUT operation.
</div>
<p>An additional option has been added <code>options.emulateREST: true || false</code>, which is being passed to the Request instance. If your server has no CRUD mapping, emulation can be enabled so everything will go through POST/GET requests with <code>_method</code> containing the original intent.</p>
<p><code>options.headers</code> (object) is an extra argument that gets passed to the Request instance, allowing you to set whatever you want for CSRF or CORS on your Model.Sync calls.</p>
<p><code>options.request</code> (Class) is a constructor to instantiate into <code>this.request</code> on the model instance. It needs to support all the APIs and functionality of MooTools' Request.JSON.</p>
<h3 id="epitomemodelsync/sync">sync</h3>
<hr>
<div class="alert">
<p>
Expects optional arguments: <code>(String) method</code>, <code>(Object) model</code><em>
</em></p>
<p>
_Events: <code>sync: function(responseObj, method, options) {}</code>
</p>
</div>
<p>Sync acts as a proxy/interface to the XHR instance in the model <code>this.request</code> A method can be one of the following:</p>
<blockquote>
<p>get, post, create, read, delete, update</p>
</blockquote>
<p>If no method is supplied, a <code>read</code> is performed.</p>
<p>The second argument <code>model</code> is optional and should be a simple object. If it is not supplied, the default <code>model.toJSON()</code> is used instead.</p>
<p>As a whole, you should NOT use the sync directly but elect to use the API methods for each specific request task.</p>
<p><strong>WARNING:</strong> Epitome is a REST framework. Please make sure you are returning a valid JSON string or 204 (no content) after all requests -
otherwise, the save events may not fire. Additionally, try to ensure <code>application/json</code> content type of your response, although
<code>text/html</code> and <code>text/plain</code> will also be accepted, as long as the responseText can be parsed into an Object.</p>
<h3 id="epitomemodelsync/postprocessor">postProcessor</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) response</code></em>
</p>
<p>
<em>Expected return: <code>(Object) response</code></em>
</p>
</div>
<p>A method that you can extend in your definition of Models for doing any post-processing of data <code>returned</code> by sync from the server. For example:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">postProcessor: function(response) {
// data comes back with decoration. split them first.
this.meta = response.meta;
return response.data;
}</code></pre>
<h3 id="epitomemodelsync/save">save</h3>
<hr>
<div class="alert">
<p>
<em>Expects optional arguments: <code>(String) key</code>, <code>(String) value</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>save</code>, <code>sync</code>, possibly <code>create</code>, <code>update</code></em>
</p>
</div>
<p>The save should send the contents of the model to the server for storage. If it is a model that has not been saved before or fetched from the server, it will do so via <code>create()</code>, else, it will use <code>update()</code> instead.</p>
<p>If the optional <code>key</code> => <code>value</code> pair is passed, it will set them on the model and then save the updated model.</p>
<h3 id="epitomemodelsync/preprocessor">preProcessor</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) response</code></em>
</p>
<p>
<em>Expected return: <code>(Object) response</code></em>
</p>
</div>
<p>A method that you can add to your definition of Models for doing any pre-processing of data before using <code>CREATE</code> or <code>UPDATE</code> (so, <code>save</code>) via sync to the server. For example:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">preProcessor: function(data) {
// remove local property 'meta' which the server does not like.
delete data.meta;
return data;
}</code></pre>
<h3 id="epitomemodelsync/fetch">fetch</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>fetch</code>, <code>sync</code>, <code>read</code></em>
</p>
</div>
<p>It will request the server to return the model object for the current id via a <code>.read()</code>. It will also change the status of the model (<code>model.isNewModel</code>) to false, meaning <code>.save()</code> will never use <code>.create()</code>. The fetch event will fire once the response object has been returned. The response object is then merged with the current model via a <code>.set</code>, it won't empty your data. To do so, you need to issue a <code>.empty()</code> first.</p>
<h2 id="epitomecollection">Epitome.Collection</h2>
<p>Epitome collections are in essence, an Array-like Class that can contain multiple Models. It has a basic model prototype and adding and removing of models works either based upon passing a simple data has or an actual Model instance. When a model is in a collection, it observes all of the model events and fires them on the collection instance. It also allows for filtering, mapping, sorting and many other more convenience methods.</p>
<h3 id="epitomecollection/constructor-initialize">constructor (initialize)</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Array) models / objects</code> (or a single model /object), <code>(Object) options</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>ready</code></em>
</p>
</div>
<p>The constructor method will accept a large variety of arguments. You can pass on either an Array of Models or an Array of Objects or a single Model or a single Object. You can also pass an empty Array and populate it later. Typical Collection prototype definition looks something like this:</p>
<pre class="prettyprint linenums"><code class="lang-ace">var userModel = new Class({Extends: Epitome.Model}),
usersCollection = new Class({
Extends: Epitome.Collection,
model: userModel // or Epitome.Model by default
});
var users = new usersCollection([{
id: 'bob'
}], {
onChange: function(model, props) {
console.log('model change', model, props);
},
onReady: function() {
console.log('the collection is ready');
}
});</code></pre>
<p>For reference purposes, each Model that enters a collection needs to have a <code>cid</code> - collection id. If the Model has an <code>id</code>, that is preferred. Otherwise, a <code>cid</code> will be generated. If the Model gets an <code>id</code> later on, the <code>cid</code> will not be changed.</p>
<div class="alert alert-info">
<p>
<em>Please note that Collections <strong>observe</strong> and bubble <strong>all</strong> model events. For instance, if a Model fires <code>change</code>, the Collection instance will fire <code>onChange</code>, passing the model as <code>arguments[0]</code> and then keeping the rest of the arguments in their original order. For the purposes of implementing this, a decorated local copy of each Model's <code>.fireEvent</code> method is created instead of the one from Class.Event prototype. Once a Model stops being a member of collections, the original <code>fireEvent</code> is restored by deleting the local method on the Model instance.</em>
</p>
</div>
<h3 id="epitomecollection/reset">reset</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Mixed) model(s)</code> , <code>(Boolean) quiet</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>reset: function() {}</code></em>
</p>
</div>
<p>Initial collection population and</p>
<h3 id="epitomecollection/addmodel">addModel</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Mixed) model</code> , <code>(Boolean) replace</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>add: function(model, cid) {}</code></em>
</p>
</div>
<p>Adding a Model to a Collection must always happen through this method. It either appends the Model instance to the internal <code>_models</code> Array or it creates a new Model and then appends it. It also starts observing the Model's events and emitting them to the Collection instance with an additional argument passed <code>Model</code>. So, if you add a Model stored in <code>bob</code> and then do <code>bob.trigger('hai', 'there')</code>, the collection will also fire an event like this: <code>this.trigger('hai', [bob, 'there']); Adding a Model also increases the</code>Collection.length` property.</p>
<p>The monitoring of the events (Observer) is done through creating a local function override / decoration of the Model's <code>fireEvent</code> method, normally inherited from the MooTools Events Class. If a model stops being a part of a collection, the override is destroyed and the default <code>fireEvent</code>.</p>
<h3 id="epitomecollection/removemodel">removeModel</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Mixed) model(s)</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>remove: function(model, cid) {}</code>, <code>reset</code></em>
</p>
</div>
<p>This method allows you to remove a single model or an array of models from the collection in the same call. For each removed model, a <code>remove</code> Event will fire. When removing of all Models is done, the collection will also fire a <code>reset</code> event, allowing you to re-render your views if you like.</p>
<p>In addition to removing the Model from the Collection, it removes the reference to the Collection in the Model's <code>collections</code> Array. If that model stops being a member of any collection, the observed <code>fireEvent</code> method is removed from the Model instance, resulting in the method from the Events Class prototype taking over.</p>
<p>Decreases the <code>Collection.length</code> property.</p>
<h3 id="epitomecollection/getmodel">getModel</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Number) id</code></em>
</p>
<p>
<em>Returns: <code>modelInstance</code> or <code>null</code></em>
</p>
</div>
<p>Returns a model based upon the Array index in the Collection.</p>
<h3 id="epitomecollection/getmodelbycid">getModelByCID</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(String) cid</code></em>
</p>
<p>
<em>Returns: <code>modelInstance</code> or <code>null</code></em>
</p>
</div>
<p>Performs a search in the collection by <code>cid</code> (Collection id). Returns found Model instance or <code>null</code> if no match is found.</p>
<h3 id="epitomecollection/getmodelbyid">getModelById</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(String) id</code></em>
</p>
<p>
<em>Returns: <code>modelInstance</code> or <code>null</code></em>
</p>
</div>
<p>Performs a search in the collection by the Model's <code>id</code> via the standard <code>getter</code>. Returns found Model instance or <code>null</code> if no match is found.</p>
<h3 id="epitomecollection/tojson">toJSON</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>modelsData</code></em>
</p>
</div>
<p>Returns an array of the applied <code>toJSON</code> method on all Models in the collection.</p>
<h3 id="epitomecollection/empty">empty</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>remove</code>, <code>reset</code>, <code>empty</code></em>
</p>
</div>
<p>Applies <code>this.removeModel</code> to all Models of the collection. Fires <code>empty</code> when done - though before that, a <code>remove</code> and <code>reset</code> will fire, see <a href="#epitomecollection/removemodel">removeModel</a></p>
<h3 id="epitomecollection/sort">sort</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: (Mixed) how</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>sort</code></em>
</p>
</div>
<p>Sorting is quite flexible. It works a lot like <code>Array.prototype.sort</code>. By default, you can sort based upon strings that represent keys in the Models. You can also stack up secondary, trinary etc sort keys in case the first one is equal. For example:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">users.sort('name');
// descending order pseduo
users.sort('name:desc');
// by type and then birthdate in reverse order (oldest first)
users.sort('type,birthdate:desc');</code></pre>
<p>Sorting also allows you to pass a function you define yourself as per the <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort">Array.prototype.sort</a> interface. When done, it will fire a <code>sort</code> event.</p>
<h3 id="epitomecollection/reverse">reverse</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: none</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>sort</code></em>
</p>
</div>
<p>Reverses sort the order of Models in the collection. Fires a <code>sort</code> event, not <code>reverse</code></p>
<h3 id="epitomecollection/find">find</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: (String) expression</em>
</p>
<p>
<em>Returns: <code>(Array) MatchingModelObjects</code></em>
</p>
</div>
<p>This is an experimental API and is subject to change without notice. <code>Collection.find</code> is currently powered by the MooTools <code>Slick.parse</code> engine. This means you can
search through your Collection for Models by attributes and <code>#ids</code> like you would search in a CSS selector.</p>
<p>For example:</p>
<pre class="prettyprint linenums"><code class="lang-ace">var collection = new Epitome.Collection([{
name: 'Bob',
id: 2
}, {
name: 'Angry Bob',
id: 3
}]);
collection.find('[name]'); // where name is defined.
collection.find('[name=Bob]'); // where name is exactly Bob.
collection.find('[name*=Bob]'); // where name contains Bob.
collection.find('[name$=Bob]'); // where name ends on Bob.
collection.find('[name^=Bob]'); // where name starts with Bob.
collection.find('[name=Bob],[name^=Angry]'); // name Bob OR starting with Angry.
collection.find('[name=Bob][id]'); // name Bob AND to have an id
collection.find('#2[name=Bob],#3'); // (name Bob AND id==2) OR id==3
collection.find('[name=Bob][id=2]'); // name Bob AND id==2</code></pre>
<p>Supported operators are <code>=</code> (equals), <code>!=</code> (not equal), <code>*=</code> (contains), <code>$=</code> (ends on), <code>^=</code> (starts with). Currently, you cannot reverse a condition by adding <code>!</code> or <code>not:</code> - in fact, pseudos are not supported yet. Find is just sugar and for more complicated stuff, you can either extend it or use <code>filter</code> instead.</p>
<p>A new 'feature' has been added that alows you to quickly select deeper object properties by treating any parent keys as tags. For instance:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">var collection = new Epitome.Collection([{
name: 'Bob',
permissions: {
edit: true
}
}, {
name: 'Angry Bob',
permissions: {
edit: false
}
}]);
collection.find('permissions[edit]'); // all where there is an edit property
collection.find('permissions[edit=true]'); // all where there edit is true</code></pre>
<p>However, this is more of a syntactic sugar than convention. It won't allow you to do complex CSS-like selections as you cannot combine 'tag' with properties. This means you cannot do <code>permissions[edit][name=Bob]</code> as the context changes to the permissions property. This kind of structure is possibly an anti-pattern anyway, try to keep your models flat.</p>
<h3 id="epitomecollection/findone">findOne</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: (String) expression</em>
</p>
<p>
<em>Returns: <code>(Model) First matching Model instance or null</code></em>
</p>
</div>
<p>Useful for getting a single Model via the <code>.find</code>, this method will return the first matched Model or null if none found.</p>
<pre class="prettyprint linenums"><code class="lang-javascript">var bob = collection.findOne('[name=bob]');
// if found, set
bob && bob.set('name','Robert');</code></pre>
<h3 id="epitomecollection/array-helpers">Array helpers</h3>
<p>The following Array methods are also available directly on the Collection instances:</p>
<ul>
<li>forEach</li>
<li>each</li>
<li>invoke</li>
<li>filter</li>
<li>map</li>
<li>some</li>
<li>indexOf</li>
<li>contains</li>
<li>getRandom</li>
<li>getLast</li>
</ul>
<p>For more information, see <a href="http://mootools.net/docs/core/Types/Array">Mootools Array Type</a></p>
<h3 id="epitomecollection/collection-properties">Collection properties*</h3>
<h4>_models</h4>
<hr>
<p>Each Collection instance has an Array property called <code>_models</code> that contains all referenced Model instances. Even though it is not a real private property, it is recommended you do not alter it from outside of the API.</p>
<h4>length</h4>
<hr>
<p>Tries to always reference the length of <code>_models</code>.</p>
<h4>model</h4>
<hr>
<p>Each Collection prototype has that property that references a Model prototype constructor. When data is being received in raw format (so, simple Objects), Models are being created by instantiating the stored constructor object in <code>this.model</code>.</p>
<h2 id="epitomecollectionsync">Epitome.Collection.Sync</h2>
<p>The Sync Class is just a layer on top of the normal <a href="#epitomecollection">Epitome.Collection</a>. It extends the default Collection prototype and adds a Request instance that can retrieve an Array of Model data from a server and add / update the Collection after.</p>
<h3 id="epitomecollectionsync/constructor-initialize">constructor (initialize)</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Array) models / objects</code> (or a single model /object), <code>(Object) options</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>ready</code></em>
</p>
</div>
<p>In terms of differences with the original prototype, the <code>options</code>, needs just one extra key: <code>urlRoot</code>, which should contain the absolute or relative URL to the end-point that can return the Model data.</p>
<h3 id="epitomecollectionsync/fetch">fetch</h3>
<hr>
<div class="alert">
<p>
<em>Expects optional arguments: <code>(Boolean) refresh</code>, <code>(Object) queryParams</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>fetch</code></em>
</p>
</div>
<p>When called, it will asynchronously try to go and fetch Model data. When data arrives, Models are reconciled with the Models in the collection already by <code>id</code>. If they exist already, a <code>set()</code> is called that will merge new data into the Model instance and fire <code>change</code> events as appropriate. If the optional <code>refresh</code> argument is set to true, the current collection will be emptied first via <a href="#epitomecollection/empty">empty</a>.</p>
<p>Returns the instance 'now' but because it is async, applying anything to the collection before the <code>fetch</code> event has fired may have unexpected results.</p>
<p>The <code>queryParams</code> object, which is also optional, allows you to pass on any <code>GET</code> arguments to the <code>baseUrl</code>. If your default endpoint looks like this:</p>
<p><code>/comments/2/</code> and you call <code>collection.fetch(false, {page: 2})</code>, it will actually get <code>/comments/2/?page=2</code>.</p>
<h3 id="epitomecollectionsync/postprocessor">postProcessor</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Mixed) response</code></em>
</p>
<p>
<em>Expected return: <code>(Array) response</code></em>
</p>
</div>
<p>A method that you can extend in your definition of Epitome.Collection.Sync for doing any pre-processing of data returned by sync from the server. For example:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">postProcessor: function(response) {
// data comes back with decoration. split them first.
// { meta: { something: 'here' }, models: [] }
this.meta = response.meta;
return response.models;
}</code></pre>
<h2 id="epitomeview">Epitome.View</h2>
<p>The view is a pretty loose binding around a HTMLElement, it does not try to do much by default. It essentially binds the element to either a Model or a Collection, listening and propagating events that they fire in order to be able to react to them. The expectation is that a <code>render</code> method will be defined that uses the data to output it in the browser. The render can be called based upon change or reset events as needed.</p>
<h3 id="epitomeview/constructor-initialize">constructor (initialize)</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) options</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>ready</code></em>
</p>
</div>
<p>A single argument in the shape of an <code>options</code> Object is passed to the constructor of the View. It is expected to have special 'mutator'-like properties and key properties that it stores for future use.</p>
<p>A simple example would look like this:</p>
<pre class="prettyprint linenums"><code class="lang-ace">// define the View prototype
var testView = new Class({
Extends: Epitome.View,
render: function() {
// have a render.
this.empty();
this.element.set('html', this.template(this.model.toJSON()));
this.parent();
return this;
},
doEmpty: function() {
this.model.empty();
this.render();
}
});
var testInstance = new testView({
model: new Epitome.Model({name: 'View fun'}),
element: 'main',
template: 'I am a template and I am called <a href="#" class="task-remove"><%=name%></a><br/><button class="change-one">empty it</button>',
// event binding
events: {
'click:relay(a.task-remove)': 'emptyModel', // emit this event to instance
'click:relay(button.change-one)': 'changeModel'
},
onReady: function() {
this.render();
},
'onChange:model': function(){
this.model.set('name', new Date().getTime());
this.render();
},
onEmptyModel: function(event, element) {
event && event.stop && event.stop();
console.log(element); // a.task-remove
this.doEmpty();
}
});</code></pre>
<p>The key <code>options</code> are:</p>
<ul>
<li><code>element</code> - a String id or an element to bind events to and reference</li>
<li><code>model</code> - optional Model instance structure to bind to. Exchangeable with <code>collection</code></li>
<li><code>collection</code> - optional Collection instance to bind to. Exchangeable with <code>model</code></li>
<li><code>template</code> - a String of raw HTML that defines the raw template to use in output.</li>
<li><code>events</code> - an Object with MooTools style event bindings to apply to the <code>element</code>, delegated or not. values are implied event handlers on the instance</li>
<li><code>onEventHandlers</code> - code that reacts to various events that the instance fires.</li>
</ul>
<h3 id="epitomeview/render">render</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: unknown</em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
<p>
<em>Events: <code>render</code></em>
</p>
</div>
<p>It is essential that this method is defined in your View prototype Object definition. It does not assume to do anything by default, you need to define how the output takes place and how your data is being used. For convenience, it has access to either <code>this.model</code> or <code>this.collection</code> as the source of data that can be be passed to the <a href="#epitomeview/template">template</a> method. It is expected that at the bottom of your definition, <code>this.parent()</code> is called in order for the <code>render</code> event to fire.</p>
<h3 id="epitomeview/setelement">setElement</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Mixed) element</code>, optional <code>(Object) events</code></em>
</p>
<p>
<em>Returns: <code>this</code></em>
</p>
</div>
<p>A public method that allows you to change or set an element that powers a view. If called the first time, it will get the Element (through <code>document.id()</code>) and save the reference in <code>this.element</code>. If an events object is passed, it will bind the events. If called a second time, it will unbind all events on the old element, change the element reference and rebind new events, if supplied.</p>
<h3 id="epitomeview/template">template</h3>
<hr>
<div class="alert">
<p>
<em>Expects arguments: <code>(Object) data</code>, optional <code>(String) template</code></em>
</p>
<p>
<em>Returns: compiled template or function.</em>
</p>
</div>
<p>A simple sandbox function where you can either use the Epitome.Template templating engine or call an external engine like Mustache, Handlebars, Hogan etc. The second argument is optional and if not supplied, it will revert to <code>this.options.template</code> instead.</p>
<p>An example override to make it work with Mustache would be:</p>
<pre class="prettyprint linenums"><code class="lang-javascript">var myView = new Class({
Extends: Epitome.View,
template: function(data, template) {
template = template || this.options.template;
return Mustache.render(template, data);
},
render: function() {
this.element.set('html', this.template({name:'there'}, 'Hello {{name}}'));
}
});</code></pre>
<p>You can change the View prototype to always have Mustache in your views. For example, via AMD/RequireJS, you could do a
small module that deals with the prototyping of the default View constructor. Say, <code>epitome-view-mustache.js</code></p>
<pre class="prettyprint linenums"><code class="lang-javascript">
define(['epitome/epitome-view'], function(View){
// prototype it for everyone to use mustache in every view.
View.implement({
template: function(data, template) {
// refactor this to work with any other template engine in your constructor
template = template || this.options.template;
return Mustache.render(template, data);
}
});