-
Notifications
You must be signed in to change notification settings - Fork 3
/
docs.html
1367 lines (1278 loc) · 81.2 KB
/
docs.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 lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Pink - Single Page App framework</title>
<meta name="description" content="">
<meta name="author" content="ink, cookbook, recipes">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<!-- load inks css from the cdn -->
<link rel="stylesheet" type="text/css" href="http://cdn.ink.sapo.pt/3.1.1/css/ink.css">
<link rel="stylesheet" type="text/css" href="http://cdn.ink.sapo.pt/3.1.1/css/font-awesome.css">
<link rel="stylesheet" type="text/css" href="content/css/docs.css">
<link rel="stylesheet" type="text/css" href="libs/prettify/prettify.css">
<link rel="stylesheet" type="text/css" href="libs/prettify/themes/ghink2.css">
<!-- load inks javascript files from the cdn -->
<script type="text/javascript" src="http://cdn.ink.sapo.pt/3.1.1/js/ink-all.js"></script>
<script type="text/javascript" src="libs/prettify/prettify.js"></script>
</head>
<body>
<div class="ink-grid">
<!--[if lte IE 9 ]>
<div class="ink-alert basic">
<button class="ink-dismiss">×</button>
<p>
<strong>You are using an outdated Internet Explorer version.</strong>
Please <a href="http://browsehappy.com/">upgrade to a browser</a> to improve your web experience.
</p>
</div>
-->
<header id="mainMenu">
<h1 class="hide-tiny hide-small"><a href="index.html"><img src="content/img/logo-small.png" height="50" alt=""></a><small>Single page app framework</small></h1>
<nav class="ink-navigation quarter-top-space">
<ul class="menu horizontal black hide-tiny hide-small">
<li><a href="get-started.html">Get started</a></li>
<li class="active"><a href="docs.html">Docs</a></li>
<li><a href="samples.html">Sample apps</a></li>
<li><a href="https://github.com/sapo/pink" target="_blank">GitHub</a></li>
</ul>
<ul class="menu horizontal black hide-medium hide-large hide-xlarge">
<li id="verticalMenuTrigger" class="menu-bars" data-target="#verticalMenuMenu">
<a href="#"><i class="fa fa-bars"></i>
</a>
<li class="small-logo-right">
<a href="index.html"><img src="content/img/logo-small.png" height="30" alt=""></a>
</li>
</ul>
</nav>
<nav class="ink-navigation hide-all" id="verticalMenuMenu">
<ul class="menu vertical black">
<li><a href="get-started.html">Get started</a></li>
<li class="active"><a href="docs.html">Docs</a></li>
<li><a href="samples.html">Sample apps</a></li>
<li><a href="https://github.com/sapo/pink" target="_blank">GitHub</a></li>
</ul>
</nav>
</header>
<div id="docsContainer" class="column-group gutters large-100 xlarge-100">
<div id="docMenu">
<h2>Documentation</h2>
<nav>
<ol>
<li>
<a href="#app">Pink application interface</a>
<ol>
<li>
<a href="#appAttributes" class="sub-menu" data-target="#appAttributesMenu">Attributes</a>
<ol id="appAttributesMenu" class="hide-all">
<li><a href="#modalModule">modalModule</a></li>
<li><a href="#alertModule">alertModule</a></li>
<li><a href="#infoModule">infoModule</a></li>
<li><a href="#rootRoute">rootRoute</a></li>
<li><a href="#undefinedRoute">undefinedRoute</a></li>
<li><a href="#mainModule">mainModule</a></li>
<li><a href="#definedRoutes">definedRoutes</a></li>
</ol>
</li>
<li>
<a href="#appMethods" class="sub-menu" data-target="#appMethodsMenu">Methods</a>
<ol id="appMethodsMenu" class="hide-all">
<li><a href="#showInfoToast">showInfoToast</a></li>
<li><a href="#showErrorToast">showErrorToast</a></li>
<li><a href="#showSuccessToast">showSuccessToast</a></li>
<li><a href="#showModalWindow">showModalWindow</a></li>
<li><a href="#showMiniModalWindow">showMiniModalWindow</a></li>
<li><a href="#showSmallModalWindow">showSmallModalWindow</a></li>
<li><a href="#showLargeModalWindow">showLargeModalWindow</a></li>
<li><a href="#showConfirm">showConfirm</a></li>
<li><a href="#showInfoBox">showInfoBox</a></li>
<li><a href="#showStandby">showStandby</a></li>
<li><a href="#hideStandby">hideStandby</a></li>
<li><a href="#listVisibleRoutes">listVisibleRoutes</a></li>
<li><a href="#listInvisibleRoutes">listInvisibleRoutes</a></li>
<li><a href="#addVisibleRoute">addVisibleRoute</a></li>
<li><a href="#addInvisibleRoute">addInvisibleRoute</a></li>
<li><a href="#addCustomSignals">addCustomSignals</a></li>
<li><a href="#navigateTo">navigateTo</a></li>
<li><a href="#navigateToStart">navigateToStart</a></li>
<li><a href="#listPluginModules">listPluginModules</a></li>
<li><a href="#run">run</a></li>
<li><a href="#ready">ready</a></li>
</ol>
</li>
</ol>
</li>
<li><a href="#routing">Routing</a></li>
<li><a href="#signals">Signals</a></li>
<li>
<a href="#uiModules" class="sub-menu" data-target="#uiModulesMenu">UI Modules</a>
<ol id="uiModulesMenu" class="hide-all">
<li><a href="#modules">Custom modules</a></li>
<li><a href="#modalwindow">ModalWindow</a></li>
<li><a href="#grid">Grid</a></li>
<li><a href="#paginator">Paginator</a></li>
<li><a href="#autocomplete">AutoComplete</a></li>
<li><a href="#tooltip">Tooltip</a></li>
<li><a href="#dragdrop">Drag&Drop</a></li>
<li><a href="#kanban">Kanban</a></li>
<li><a href="#carousel">Carousel</a></li>
<li><a href="#tabs">Tabs</a></li>
<li><a href="#datepicker">DatePicker</a></li>
<li><a href="#toggle">Toggle</a></li>
</ol>
</li>
<li><a href="#plugins">Plugins</a></li>
<li><a href="#bundles">Bundling</a></li>
</ol>
</nav>
</div>
<div id="docArea">
<article class="docs-article">
<a class="bookmark" id="app"></a>
<header>
<h2 class="push-left">Application Interface</h2>
</header>
<summary>
The main module of every Pink application must inherit its prototype from the <code>Pink.App</code> module.
The app's modules should require this module in their dependencies to access the framework's public interface.
</summary>
<br>
<h4><em>Attributes</em></h4>
<ul class="unstyled">
<li>
<a class="bookmark" id="modalModule"></a>
<strong>modalModule : object</strong><br>
<div class="details">
Exposes a generic modal viewmodel for databinding, usually used in the application main layout.
Must be bound to a DOM element for the app's <code>showModalWindow</code> method to work.
<blockquote><pre class="prettyprint lang-html"><div data-bind="module: app.modalModule"></div></pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="alertModule"></a>
<strong>alertModule : object</strong><br>
<div class="details">
Exposes a confirm modal viewmodel for databinding, usually used in the application main layout.
Must be bound to a DOM element for the app's <code>showConfirm</code> method to work.
<blockquote><pre class="prettyprint lang-html"><div data-bind="module: app.alertModule"></div></pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="infoModule"></a>
<strong>infoModule : object</strong><br>
<div class="details">
Exposes an informative modal viewmodel for databinding, usually used in the application main layout.
Must be bound to a DOM element for the app's <code>showInfoBox</code> method to work.
<blockquote><pre class="prettyprint lang-html"><div data-bind="module: app.infoModule"></div></pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="rootModule"></a>
<strong>rootRoute : string</strong><br>
<div class="details">
The default route to show when the app is loaded, or the url fragment is empty.
</div>
</li>
<li>
<a class="bookmark" id="undefinedRoute"></a>
<strong>undefinedRoute : string</strong><br>
<div class="details">
The route to show when there's an non existent route on the url fragment.
</div>
</li>
<li>
<a class="bookmark" id="mainModule"></a>
<strong>mainModule: object</strong><br>
<div class="details">
Exposes the active route's module (taken from the url fragment) for databinding, usually used in the application main layout.
Must be bound to a DOM element.
<blockquote><pre class="prettyprint lang-html"><section data-bind="module: mainModule"/></pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="definedRoutes"></a>
<strong>definedRoutes: object</strong><br>
<div class="details">
Exposes the application's defined routing parameters for databinding, usually used in the application main menu.
<blockquote><pre class="prettyprint lang-html">
<ul id="mainMenuDropDown" class="menu vertical white">
<!-- ko foreach: definedRoutes.visibleRoutes -->
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: '#'+hash }, html: caption"></a>
</li>
<!-- /ko -->
</ul>
</pre></blockquote>
</div>
</ul>
<br>
<h4><em>Methods</em></h4>
<ul class="unstyled">
<li>
<a class="bookmark" id="showInfoToast"></a>
<strong>showInfoToast(message) : void</strong><br>
<div class="details">
Shows a temporary notification panel in the UI for information purposes.<br>
<span class="note"><i class="icon-exclamation-sign"></i> The app's DOM must have an element with an id <code>toastPanel</code>, usually in the main layout.</span>
<blockquote><pre class="prettyprint lang-js">app.showInfoToast('Hello there!');</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showErrorToast"></a>
<strong>showErrorToast(message) : void</strong><br>
<div class="details">
Shows a temporary notification panel in the UI for application error purposes.<br>
<span class="note"><i class="icon-exclamation-sign"></i> The app's DOM must have an element with an id <code>toastPanel</code>, usually in the main layout.</span>
<blockquote><pre class="prettyprint lang-js">app.showErrorToast('Oops, an error occurred :(');</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showSuccessToast"></a>
<strong>showSuccessToast(message) : void</strong><br>
<div class="details">
Shows a temporary notification panel in the UI to notify the user that some action completed with success.<br>
<span class="note"><i class="icon-exclamation-sign"></i> The app's DOM must have an element with an id <code>toastPanel</code>, usually in the main layout.</span>
<blockquote><pre class="prettyprint lang-js">app.showSuccessToast('Document saved');</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showModalWindow"></a>
<strong>showModalWindow(title, moduleName, params, modalStyle) : void</strong><br>
<div class="details">
Shows a modal window dialog with a custom module in the contents area.<br>
<ul>
<li><strong>title</strong> - string containing the modal's title</li>
<li><strong>moduleName</strong> - string containing a valid Pink UI module name (eg. 'App.Tasks.EditDialog')</li>
<li>
<strong>params</strong> - object with the parameters to pass to the module<br>
<span class="note"><i class="icon-exclamation-sign"></i> Goto the <a href="#modalwindow">ModalWindow</a> section for more detais on the modal's contents module interface</span>
</li>
<li><strong>modalStyle</strong> - object with modal window style parameters<br> <code class="prettify lang-js">{width : string, height : string, cancelVisible : bool, confirmCaption : string}</code></li>
</ul>
<span class="note"><i class="icon-exclamation-sign"></i> See the <code>modalModule</code> attribute</span><br>
<blockquote><pre class="prettyprint lang-js">app.showModalWindow('Edit item', 'App.Item.Edit', {item: item}, {width: '600px', height: '400px'});</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showMiniModalWindow"></a>
<strong>showMiniModalWindow(title, moduleName, params, modalStyle) : void</strong><br>
<div class="details">
Shows a mini modal window with a custom module in the content area.<br>
<span class="note"><i class="icon-exclamation-sign"></i> See <code>showModalWindow</code> for details and example usage</span>
</div>
</li>
<li>
<a class="bookmark" id="showSmallModalWindow"></a>
<strong>showSmallModalWindow(title, moduleName, params, modalStyle) : void</strong><br>
<div class="details">
Shows a small modal window with a custom module in the content area.<br>
<span class="note"><i class="icon-exclamation-sign"></i> See <code>showModalWindow</code> for details and example usage</span>
</div>
</li>
<li>
<a class="bookmark" id="showLargeModalWindow"></a>
<strong>showLargeModalWindow(title, moduleName, params, modalStyle) : void</strong><br>
<div class="details">
Shows a large modal window with a custom module in the contents area.<br>
<span class="note"><i class="icon-exclamation-sign"></i> See <code>showModalWindow</code> for details and example usage</span>
</div>
</li>
<li>
<a class="bookmark" id="showConfirm"></a>
<strong>showConfirm(title, message, confirmCallback, cancelCallback) : void</strong>
<div class="details">
Shows a modal window for the user to respond to a confirm action.
<ul>
<li><strong>title</strong> - string containing the modal's title</li>
<li><strong>message</strong> - string with the message for the user</li>
<li><strong>confirmCallback</strong> - function to be called if the user confirms the action</li>
<li><strong>cancelCallback</strong> - function to be called if the user cancels the action</li>
</ul>
<blockquote><pre class="prettyprint lang-js">app.showConfirm('Please confirm...', 'Remove the item?', confirmHandler, cancelHandler);</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showInfoBox"></a>
<strong>showInfoBox(title, message) : void</strong>
<div class="details">
Shows a modal window with a simple message.
<ul>
<li><strong>title</strong> - string containing the modal's title</li>
<li><strong>message</strong> - string with the message for the user</li>
</ul>
<blockquote><pre class="prettyprint lang-js">app.showInfoBox('User info', 'Here is some extra info');</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="showStandby"></a>
<strong>showStandby() : void</strong>
<div class="details">
Shows a standby panel that disables user input.<br>
<span class="note"><i class="icon-exclamation-sign"></i> There must exist a DOM element with the id <code>standbyLightBox</code> for this method to work</span>
</div>
</li>
<li>
<a class="bookmark" id="hideStandby"></a>
<strong>hideStandby() : void</strong>
<div class="details">
Hides the standby panel.<br>
<span class="note"><i class="icon-exclamation-sign"></i> There must exist a DOM element with the id <code>standbyLightBox</code> for this method to work</span>
</div>
</li>
<li>
<a class="bookmark" id="listVisibleRoutes"></a>
<strong>listVisibleRoutes() : array</strong>
<div class="details">
This method should be overriden by the application main module, to return the routes to be
rendered in the main application menu.
<blockquote><pre class="prettyprint lang-js">
Module.prototype.listVisibleRoutes = function() {
return [
{isActive: ko.observable(true), caption: 'To-do', hash: 'todo', module: 'App.Tasks.Home', arguments: {filter: 'todo'}},
{isActive: ko.observable(false), caption: 'Incomplete', hash: 'incomplete', module: 'App.Tasks.Home', arguments: {filter: 'incomplete'}},
{isActive: ko.observable(false), caption: 'Completed', hash: 'completed', module: 'App.Tasks.Home', arguments: {filter: 'complete'}}
];
};</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="listInvisibleRoutes"></a>
<strong>listInvisibleRoutes() : array</strong>
<div class="details">
This method should be overriden by the application main module, to return the routes not visible
in the application's main menu.
<blockquote><pre class="prettyprint lang-js">
Module.prototype.listInvisibleRoutes = function() {
return [
{hash: 'users\\?search=:search', module: 'App.Example.ListUsers', parentModule: 'App.Example.ListUsers'}
];
};</pre></blockquote>
<span class="note"><i class="icon-exclamation-sign"></i> If the invisible route's <code>parentModule</code> attribute is defined, it's used to set the parent module's route active flag to <code>true</code>.</span>
</div>
</li>
<li>
<a class="bookmark" id="addVisibleRoute"></a>
<strong>addVisibleRoute(route) : void</strong>
<div class="details">
Allows a plugin to add a new visible route.
The plugin should call this method on its initialization code
<blockquote><pre class="prettyprint lang-js">app.addVisibleRoute({isActive: ko.observable(false), caption: 'Export', hash: 'export', module: 'App.Tasks.Plugins.Export'})</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="addInvisibleRoute"></a>
<strong>addInvisibleRoute(route) : void</strong>
<div class="details">
Allows a plugin to add a new invisible route.
The plugin should call this method on its initialization code
<blockquote><pre class="prettyprint lang-js">app.addInvisibleRoute({caption: 'Export item', hash: 'export\\?item=:item', module: 'App.Tasks.Plugins.ExportItem'})</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="addCustomSignals"></a>
<strong>addCustomSignals() : void</strong>
<div class="details">
Should be overriden in the application's main module, to define custom client side events for application UI logic.
<blockquote><pre class="prettyprint lang-js">
Module.prototype.addCustomSignals = function() {
this.signals.taskAdded = new Signal();
this.signals.taskUpdated = new Signal();
};
</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="navigateTo"></a>
<strong>navigateTo(path, options) : void</strong>
<div class="details">
Instructs the router to navigate to a new route inside the app.
<ul>
<li><strong>path</strong> - string with url fragement</li>
<li><strong>options</strong> - object <code>{silent: bool}</code> allows to change the url without the routing logic</li>
</ul>
<blockquote><pre class="prettyprint lang-js">app.navigateTo('#home');</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="navigateToStart"></a>
<strong>navigateToStart() : void</strong>
<div class="details">
Instructs the router to navigate to the route defined in the <code>rootRoute</code> attribute.
<blockquote><pre class="prettyprint lang-js">app.navigateToStart();</pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="listPluginModules"></a>
<strong>listPluginModules() : array</strong>
<div class="details">
Should be overriden by the apps main module to return an array with the names of the modules to be loaded on the app's bootstrap.
</div>
</li>
<li>
<a class="bookmark" id="run"></a>
<strong>run() : void</strong>
<div class="details">
Must be called on the main page to start the application bootstrap process.
<blockquote><pre class="prettyprint lang-html">
<script type="text/javascript">
Ink.requireModules(['App.Tasks'], function(app) {
app.run();
});
</script> </pre></blockquote>
</div>
</li>
<li>
<a class="bookmark" id="ready"></a>
<strong>ready() : void</strong>
<div class="details">
Override this method to add your own custom initialization logic.
If you override this method, you must call <code>start()</code> when ready.
<blockquote><pre class="prettyprint lang-js">
Module.prototype.ready = function() {
var self=this;
Ink.requireModules(['App.Images.View'], function() {
self.start();
});
}; </pre></blockquote>
</div>
</li>
</ul>
</article>
<article>
<a class="bookmark" id="routing"></a>
<header>
<h2 class="push-left">Routing</h2>
</header>
<p>
Pink provides an hash based routing module (Routie), that is used to manage the view change logic.
The routing callback infers what is the active module from the window url hash fragment by looking at the application's defined visible and invisible routes.
</p>
<p>
All routes have a <code>hash</code> attribute that can be parameterized (eg. for invisible routes), a <code>module</code> attribute with the name of the
Pink UI module to load, and an optional <code>arguments</code> attribute that can contain extra options to pass to the module.
</p>
<p>
The visible routes, should be used in the application layout module, to build a menu, by binding to the app's
<a href="#definedRoutes"><code>definedRoutes.visibleRoutes</code></a> attribute. They can be defined in the app's
<a href="#listVisibleRoutes"><code>listVisibleRoutes</code></a> method, or by calling
the <a href="#addVisibleRoute"><code>addVisibleRoute</code></a> method.
</p>
<p>
The visible route has two aditional attributes named 'isActive' and 'caption, that indicate what should be the selected menu option,
and the menu item's caption respectively.
</p>
<p>
The invisible routes, defined in the app's <a href="#listInvisibleRoutes"><code>listInvisibleRoutes</code></a>, or by calling the
<a href="#addInvisibleRoute"><code>addInvisibleRoute</code></a> method, allow the definition of parameterized routes eg:
<blockquote><pre class="prettyprint lang-js">
{hash: 'users\\?search=:search', module: 'App.Example.ListUsers', parentModule: 'App.Example.ListUsers'}
</pre></blockquote>
In this example,for the route to match, the defined hash must have a search term, that is passed to the module's options.
</p>
<p>
In addition, the invisible route can provide an optional <code>parentModule</code> attribute, that tells the framework what should be the selected menu option relating to this route.
</p>
<p>
The routing callback is also responsible for updating the app's <a href="#mainModule"><code>mainModule</code></a> attribute with the module's name and parameters, notifing the "module" binding handler to reload.
</p>
<p>
The module parameters are passed as an object to the module's constructor or to module's "activate" method, in case of a class or static module respectively.
</p>
</article>
<article>
<a class="bookmark" id="signals"></a>
<header>
<h2 class="push-left">Signals</h2>
</header>
<p>
Although not required, we suggest you adopt a decoupled modular arquitecture for your Pink projects through
the use of a client side application event bus.
</p>
<p>
<b>The main beneficts of an event bus are:</b><br>
- Public interfaces for client side actions (eg. File saved event).<br>
- Each module needs only a limited knowledge about the rest of the aplication, besides the main application
interface and available signals, allowing easy implementation exchange. (eg. swap a global search widget with
a voice recognition service).<br>
- Easier unit testing because there are less dependencies between the modules, and the events can be readly
emitted in tests.<br>
<p>
Every Pink application has the following events:<br>
<ul class="unstyled">
<li><b>viewChanged(route)</b> - Emitted by the framework when the router detects a new route.</li>
<li><b>shellRendered</b> - Emitted when the application main layout is rendered (usually in the Shell module).</li>
<li><b>appReady</b> - Emitted by the framework after the initial modules and plugins have loaded</li>
</ul>
</p>
<p>
To register new events your application must assign a new instance of "Pink.Plugin.Signals_1" to app.signals.<signal-name>.
This is usally done by overriding the addCustomSignals application's method.
</p>
<blockquote><pre class="prettyprint lang-js">
Module.prototype.addCustomSignals = function() {
this.signals.taskAdded = new Signal();
this.signals.taskUpdated = new Signal();
};
</pre></blockquote>
<p>
When you need to emit a registered event (eg. when a task is updated), you should call the signal's dispatch method,
passing additional event parameters if needed.
</p>
<blockquote><pre class="prettyprint lang-js">
app.signals.taskSaved.dispatch(task);
</pre></blockquote>
<p>
To subscribe to an event your module needs to call the signal's "add" method with a callback function.
</p>
<blockquote><pre class="prettyprint lang-js">
app.signals.taskSaved.add(taskSavedHandler);
</pre></blockquote>
<p>
To remove a previous subscription call the signal's "remove" method.
</p>
<blockquote><pre class="prettyprint lang-js">app.signals.taskSaved.remove(taskSavedHandler);</pre></blockquote>
<p>
For more details about the Signals module go to the <a href="millermedeiros.github.io/js-signals/">js-signals</a> project page.
</p>
</article>
<article>
<a class="bookmark" id="modules"></a>
<header>
<h2 class="push-left">Custom UI Modules</h2>
</header>
<p>
Pink's UI modules are created by a <code>module</code> binding handler (Knockout extension), this handler dinamically loads
and binds an <a href="http://ink.sapo.pt/javascript/">Ink's class or static module</a>
file (the view model) named 'lib.js', to a template markup file (the view) named 'tpl.html'.
</p>
<p>
The new UI module must have a fully qualified package style name (eg. App.Tasks.Shell), and be placed in a directory path
with the same structure (eg. App/Tasks/Shell).
</p>
<p>
Every Pink app should have at least one UI module declared in the html page using Pink's <code>module</code> binding handler,
this module is normaly the app's main layout module.
</p>
<p>
</p>
<h3>Custom/Native Platform UI support</h3>
<p>
If you want to support multiple platforms, you can detect the current environment and set the the template file name
in the app's constructor function.
<blockquote><pre class="prettyprint lang-js">
var Module = function() {
App.call(this, 'home');
/*detect runtime environment*/
if (environment=='android') {
ko.bindingHandlers.module.templateName='tpl.android.html';
}
};
</pre></blockquote>
</p>
<h3>Module's lifecycle</h3>
<p>
The framework calls the following UI module's functions during it's lifecycle:<br>
<ul class="unstyled">
<li><b>constructor(data:object):void</b> Called on class modules after the module is loaded.</li>
<li><b>initialize(data:object):void</b> Called on static modules after the module is loaded.</li>
<li><b>afterRender(template_elements:array):void</b> Called after Knockout has data bound the module's template.</li>
<li><b>finalize:void</b> Called when the module is about to be destroyed.</li>
</ul>
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html"><div data-bind="module: str|object"></div></pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<b>module_name_str | {name: module_name_str, data: module_parameters_object, notifyReady: ready_callback, notifyBeforeDestroy: before_destroy_callback}</b>
<ul class="unstyled">
<li><b>module_name_str</b> <em>(Observable)</em> Fully qualified module name (eg. 'App.Tasks.Shell').</li>
<li><b>module_parameters_object</b> <em>(Observable)</em> Object with the parameters to pass to the modules constructor or initializer function.</li>
<li><b>ready_callback(boundElement)</b> Function called after Knockout has data bound the module's template.</li>
<li><b>before_destroy_callback(boundElement)</b> Function called when the module is about to be destroyed.</li>
</ul>
</p>
</article>
<article>
<a class="bookmark" id="modalwindow"></a>
<header>
<h2 class="push-left">ModalWindow</h2>
</header>
<p>
Pink's modal window module is a data binding wrapper around Ink's 'Ink.UI.Modal' widget, that loads an
UI module as it's content.
</p>
<p>
See Pink's app helper methods <a href="#showMiniModalWindow">showMiniModalWindow</a>,
<a href="#showSmallModalWindow">showSmallModalWindow</a> and
<a href="#shoLargeModalWindow">showLargeModalWindow</a> for a simple standard way to show UI modules in a modal dialog.
</p>
<h3>Modal contents module interface</h3>
<p>
The modal's content module constructor or initializer function, receives an object with
the follwing attributes:
<ul>
<li><b>confirmHandler</b> Should be assigned to a modal's content module method responsible for doing the logic required by the user's confirm action.
Normally this implies making an async call, calling the "params.confirmCallback()" function when the call succeeds, and calling "hide()".</li>
<li><b>confirmDisabled</b> <em>Observable</em> boolean. Usually assigned to a computed observable responsible for validating the user input, returning true if there's invalid input.</li>
<li>
<strong>params</strong>
- object with the parameters passed to the module.<br>Aditionally to custom parameters you may want to pass, there are the following special parameters:
<ul>
<li><b>confirmCallback:</b> The modal content's module, should call this function in the 'confirmHandler' method after validation.</li>
<li><b>cancelCallback:</b> Called by the framework's code after the user cancels the dialog.</li>
<li><b>taskButtons:</b> <em>Observable</em> array with buttons to be added to the dialog footer.<br>Button object: {caption: string, handler: function, cssClass: string, icon: string}</li>
</ul>
</li>
<li><b>hide</b> Function to be called by the content's module, to hide the modal dialog.</li>
</ul>
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html"><div data-bind="module: {name: 'Pink.Data.ModalWindow_1', data: modalModel}"></div></pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
Ink.createModule('App.Example.Shell', '1', ['App.Example'], function(app) {
var Module = function() {
this.modalModel = {
parent: this,
title: 'Example modal window',
modalWidth: '400px',
modalHeight: '200px',
contentModule: 'App.Example.QueryDialog',
cancelVisible: true,
confirmCaption: 'Confirm'
};
...
</pre></blockquote>
</p>
<p>
<b>{parent: module_host, title: modal_title_str, modalWidth: width_str, modalHeight: height_str, contentModule: module_name_str, cancelVisible: cancel_visible_bool, confirmCaption: confirm_caption_str}</b>
<ul class="unstyled">
<li><b>module_host</b> Object to host the "modal" interface: {modal: {show: function(params:object):void}}, reponsible to show the modal dialog, and pass parameters to the loaded module.</li>
<li><b>modal_title_str</b> <em>Observable</em> String with the modal dialog's title</li>
<li><b>width_str</b> <em>Observable</em> String with css units for the dialog's width (eg. '20em')</li>
<li><b>height_str</b> <em>Observable</em> String with css units for the dialog's height (eg. '10em')</li>
<li><b>module_name_str</b> <em>Observable</em> String with a Pink's UI module name (eg. 'App.Tasks.View') to load in the contents area</li>
<li><b>cancel_visible_bool</b> <em>Observable</em> Boolean, indicates if the cancel button is visible</li>
<li><b>confirm_caption_str</b> <em>Observable</em> String with the confirm button caption</li>
</ul>
</p>
</article>
<article>
<a class="bookmark" id="grid"></a>
<header>
<h2 class="push-left">Grid</h2>
</header>
<p>
The 'Pink.Data.Grid' module provides a Knockout binding handler to render an HTML table, fully customizable through
the use of Knockout templates, and also an extendable javascript model for
tabular data, supporting client side pagination and custom row sort handling logic (server/client).
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html">
/* Example grid binding with a custom cell renderer */
<div data-bind="simpleGrid: tableModel"></div>
<script id="shapeTemplate" type="text/html">
<img data-bind="attr: {src: $data.shape+'.png'}">
</script>
</pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
/* Example view model attribute for a table model */
Ink.createModule('App.Shapes.Home', '1', ['Pink.Data.Grid_1'], function(Grid) {
var Module = function() {
this.tableModel = new Grid({
data: [
{id: 1, shape: 'square'},
{id: 2, shape: 'circle'},
],
columns: [
{headerText: 'Id', rowText: 'id'},
{headerText: 'Shape', rowTemplate: 'shapeTemplate'},
]
});
...
</pre></blockquote>
</p>
<p>
- Grid model options object (passed to constructor):
<ul class="unstyled">
<li><b>data</b> <em>Observable</em> Array with the table data rows.</li>
<li><b>columns</b> Array of columnDefinition objects</li>
</ul>
</p>
<p>
- Column definition object:
<ul class="unstyled">
<li><b>headerText</b> String with the column header text.</li>
<li><b>headerClass</b> String with a custom column header CSS class name.</li>
<li><b>headerSortOrder</b> Function that returns a columns sort order: 'asc' for ascending ordering, 'desc' for descending
ordering and 'sort' for default sort order.</li>
<li><b>headerSortHandler</b> Function that handles the custom sorting logic.</li>
<li><b>visible</b> <em>Observable</em> boolean that indicates if the column if visible.
<li><b>cellClass</b> String with a custom column cell CSS class name.</li>
<li><b>rowHandler</b> Function to be assigned to a cell's click handler</li>
<li><b>href</b> Function that returns an href anchor attribute string for a row.</li>
<li><b>rowText</b> String with the row's data field name or Function that returns a calculated value from the row's data.</li>
<li><b>rowTemplate</b> String with a Knockout template id, for a custom cell renderer.</li>
</ul>
</p>
</article>
<article>
<a class="bookmark" id="paginator"></a>
<header>
<h2 class="push-left">Paginator</h2>
</header>
<p>
The 'Pink.Data.Paginator' module, provides a Knockout binding handler to render a pagination bar widget, and a javascript model that handles the actual
data pagination.
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html">
/* Example paginator binding */
<div data-bind="foreach: pageItems">
<div data-bind="text: $data"/>
</div>
<div data-bind="paginator: paginatedModel"></div>
</pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
/* Example view model attribute for a paginator */
Ink.createModule('App.Names.List', '1', ['Pink.Data.Paginator_1'], function(Paginator) {
var Module = function() {
this.paginatedModel = new Paginator({
data: ['Alice', 'Sara', 'John', 'Peter', 'Mark', 'Beatriz', 'Roger', 'Paul', 'Trent', 'Daniela', 'Samuel'],
pageSize: 5,
pageSizeOptionList: [5, 10, 20]
});
this.pageItems=this.paginatedModel.itemsOnCurrentPage;
...
</pre></blockquote>
</p>
<p>
- Paginator model options object (passed to constructor):
<ul class="unstyled">
<li><b>data</b> <em>Observable</em> Array with the data rows.</li>
<li><b>pageSize</b> int value with number of rows per page.</li>
<li><b>pageSizeOptionList</b> array with page sizes.</li>
</ul>
</p>
</article>
<article>
<a class="bookmark" id="autocomplete"></a>
<header>
<h2 class="push-left">AutoComplete</h2>
</header>
<p>
The 'Pink.Data.AutoComplete' module provides a Knockout binding handler that renders an autocomplete widget bound to an observable array of values.
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html">
/* Example autoComplete binding */
<input type="text" data-bind="autoComplete: users, value: selectedUserId, optionsText: 'name', optionsValue: 'id'" />
</pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
/*
Example view model
Your app must declare the dependency to the 'Pink.Data.AutoComplete' module
to register the 'autoComplete' binding handler.
*/
Ink.createModule('App.Names.List', '1', ['Pink.Data.Binding_1', 'Pink.Data.AutoComplete_1'], function(ko) {
var Module = function() {
this.users = ko.observableArray([
{id: 1, name: 'Alice'},
{id: 2, name: 'Sarah'},
{id: 3, name: 'John'},
{id: 4, name: 'Peter'},
{id: 5, name: 'Mark'},
{id: 6, name: 'Bea'},
{id: 7, name: 'Roger'},
{id: 8, name: 'Paul'},
{id: 9, name: 'Trent'},
{id:10, name: 'Daniela'},
{id:11, name: 'Sam'}
]);
this.selectedUserId = ko.observable();
...
</pre></blockquote>
</p>
</article>
<article>
<a class="bookmark" id="tooltip"></a>
<header>
<h2 class="push-left">ToolTip</h2>
</header>
<p>
'Pink.Data.Tooltip' provides a simple Ink Tooltip binding handler.
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html">
/* Example tooltip binding */
<img data-bind="tooltip: userTooltip, attr: {src: photo}">
</pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
/*
Example view model
Your app must declare the dependency to the 'Pink.Data.Tooltip' module
to register the 'tooltip' binding handler.
*/
Ink.createModule('App.Example.Home', '1', ['Pink.Data.Binding_1', 'Pink.Data.Tooltip_1'], function(ko) {
var Module = function() {
this.name = ko.observable('John Doe');
this.photo = ko.observable('john.png');
this.userTooltip = {text: this.name};
...
</pre></blockquote>
<span class="note"><i class="icon-exclamation-sign"></i> Check <a href="http://ink.sapo.pt/javascript/Ink.UI.Tooltip/">Ink.UI.Tooltip</a> Ink's docs, for details on the tooltip value object options.</span>
</p>
</article>
<article>
<a class="bookmark" id="dragdrop"></a>
<header>
<h2 class="push-left">Drag & Drop</h2>
</header>
<p>
'Pink.Data.DragDrop' provides binding handlers to render drop targets and draggable elements.
These bindings allow a clean separation of the rendered draggable and the underlying data object, meaning that the view model only needs to
handle pure js objects instead of DOM elements.
</p>
<p>
The drop targets also permit the definition of a supported data flavor, that checks if the transfered data is an instance of a specified constructor function.
</p>
<p>
The 'draggableContainer' handler builds a container widget for draggables, delegating the draggable rendering to a specified Knockout template.
</p>
<p>
The 'droppable' handler adds drop target capabilities to an element and can be combined with a 'draggableContainer' to allow full drag&drop.
</p>
<h3>Data binding</h3>
<h4><em>Binding declaration</em></h4>
<p>
<blockquote><pre class="prettyprint lang-html">
/* Example trash bin with draggableContainer & droppable bindings */
<div data-bind="draggableContainer: container" />
<div data-bind="droppable: trash" />
<script id="colorTemplate" type="text/html"><img data-bind="attr: {src: $data.name+'.png'}"/>script>
<script id="sizeTemplate" type="text/html"><img data-bind="attr: {width: $data.value}"/>script>
</pre></blockquote>
</p>
<h4><em>Binding value</em></h4>
<p>
<blockquote><pre class="prettyprint lang-js">
/*
Example view model
Your app must declare the dependency to the 'Pink.Data.DragDrop' module
to register both 'draggableContainer' and 'droppable' binding handlers.
*/
Ink.createModule('App.Example.Home', '1', ['Pink.Data.Binding_1', 'Pink.Data.DragDrop_1'], function(ko) {
var Module = function() {
var self=this;
function Color(name) {this.name = name;};
function Size(value) {this.value = value;};
this.dataItems = ko.observableArray([new Color('blue'), new Color('red'), new Color('cyan'), new Size(10), new Size(20)]);
this.trash = {
hoverClass: 'my-drop-panel',
dropHandler: function(data, index) {
var i;
data = (data.length?data:[data]);
for (i=0; i < data.length; i++) {
self.dataItems.remove(data[i]);
}
},
dataFlavor: Color
};
this.container = {
source: this.dataItems,
draggableTemplate: function(draggable) {return draggable instanceof Color ? 'colorTemplate' : 'sizeTemplate';}
};
...
</pre></blockquote>
</p>
<p>
- 'draggableContainer' options:
<ul class="unstyled">
<li><b>source</b> <em>Observable</em> array with the draggable objects.</li>
<li><b>draggableTemplate</b> String with id of template to render the draggable or function that receives the draggable and returns a corresponding template id.</li>
<li><b>dragOutHandler</b> Function that is called when an object from this container is dropped in a droppable.</li>
<li><b>afterDraggableRender</b> Function that is called after Knockout renders each draggable.</li>
</ul>
</p>
<p>
- 'droppable' options:
<ul class="unstyled">
<li><b>hoverClass</b> String with class name to add to the element when a draggable of the right flavor hovers over it.</li>
<li><b>dropHandler</b> Function to execute when a draggable is dropped in this droppable (receives selectedData as a parameter).</li>
<li><b>dataFlavor</b> Constructor function. If specified, the droppable only accepts drops of objects that are instances of this function.</li>
</ul>
</p>
</article>
<article>