ink_ui_rails 2.1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +21 -0
  3. data/README.md +39 -0
  4. data/lib/ink_ui_rails.rb +8 -0
  5. data/lib/ink_ui_rails/version.rb +3 -0
  6. data/vendor/assets/fonts/font/FontAwesome.otf +0 -0
  7. data/vendor/assets/fonts/font/fontawesome-webfont.eot +0 -0
  8. data/vendor/assets/fonts/font/fontawesome-webfont.svg +399 -0
  9. data/vendor/assets/fonts/font/fontawesome-webfont.ttf +0 -0
  10. data/vendor/assets/fonts/font/fontawesome-webfont.woff +0 -0
  11. data/vendor/assets/fonts/font/ubuntu-b-webfont.eot +0 -0
  12. data/vendor/assets/fonts/font/ubuntu-b-webfont.svg +245 -0
  13. data/vendor/assets/fonts/font/ubuntu-b-webfont.ttf +0 -0
  14. data/vendor/assets/fonts/font/ubuntu-b-webfont.woff +0 -0
  15. data/vendor/assets/fonts/font/ubuntu-bi-webfont.eot +0 -0
  16. data/vendor/assets/fonts/font/ubuntu-bi-webfont.svg +245 -0
  17. data/vendor/assets/fonts/font/ubuntu-bi-webfont.ttf +0 -0
  18. data/vendor/assets/fonts/font/ubuntu-bi-webfont.woff +0 -0
  19. data/vendor/assets/fonts/font/ubuntu-c-webfont.eot +0 -0
  20. data/vendor/assets/fonts/font/ubuntu-c-webfont.svg +245 -0
  21. data/vendor/assets/fonts/font/ubuntu-c-webfont.ttf +0 -0
  22. data/vendor/assets/fonts/font/ubuntu-c-webfont.woff +0 -0
  23. data/vendor/assets/fonts/font/ubuntu-l-webfont.eot +0 -0
  24. data/vendor/assets/fonts/font/ubuntu-l-webfont.svg +245 -0
  25. data/vendor/assets/fonts/font/ubuntu-l-webfont.ttf +0 -0
  26. data/vendor/assets/fonts/font/ubuntu-l-webfont.woff +0 -0
  27. data/vendor/assets/fonts/font/ubuntu-li-webfont.eot +0 -0
  28. data/vendor/assets/fonts/font/ubuntu-li-webfont.svg +245 -0
  29. data/vendor/assets/fonts/font/ubuntu-li-webfont.ttf +0 -0
  30. data/vendor/assets/fonts/font/ubuntu-li-webfont.woff +0 -0
  31. data/vendor/assets/fonts/font/ubuntu-m-webfont.eot +0 -0
  32. data/vendor/assets/fonts/font/ubuntu-m-webfont.svg +245 -0
  33. data/vendor/assets/fonts/font/ubuntu-m-webfont.ttf +0 -0
  34. data/vendor/assets/fonts/font/ubuntu-m-webfont.woff +0 -0
  35. data/vendor/assets/fonts/font/ubuntu-mi-webfont.eot +0 -0
  36. data/vendor/assets/fonts/font/ubuntu-mi-webfont.svg +245 -0
  37. data/vendor/assets/fonts/font/ubuntu-mi-webfont.ttf +0 -0
  38. data/vendor/assets/fonts/font/ubuntu-mi-webfont.woff +0 -0
  39. data/vendor/assets/fonts/font/ubuntu-r-webfont.eot +0 -0
  40. data/vendor/assets/fonts/font/ubuntu-r-webfont.svg +245 -0
  41. data/vendor/assets/fonts/font/ubuntu-r-webfont.ttf +0 -0
  42. data/vendor/assets/fonts/font/ubuntu-r-webfont.woff +0 -0
  43. data/vendor/assets/fonts/font/ubuntu-ri-webfont.eot +0 -0
  44. data/vendor/assets/fonts/font/ubuntu-ri-webfont.svg +245 -0
  45. data/vendor/assets/fonts/font/ubuntu-ri-webfont.ttf +0 -0
  46. data/vendor/assets/fonts/font/ubuntu-ri-webfont.woff +0 -0
  47. data/vendor/assets/fonts/font/ubuntumono-b-webfont.eot +0 -0
  48. data/vendor/assets/fonts/font/ubuntumono-b-webfont.svg +242 -0
  49. data/vendor/assets/fonts/font/ubuntumono-b-webfont.ttf +0 -0
  50. data/vendor/assets/fonts/font/ubuntumono-b-webfont.woff +0 -0
  51. data/vendor/assets/fonts/font/ubuntumono-bi-webfont.eot +0 -0
  52. data/vendor/assets/fonts/font/ubuntumono-bi-webfont.svg +242 -0
  53. data/vendor/assets/fonts/font/ubuntumono-bi-webfont.ttf +0 -0
  54. data/vendor/assets/fonts/font/ubuntumono-bi-webfont.woff +0 -0
  55. data/vendor/assets/fonts/font/ubuntumono-r-webfont.eot +0 -0
  56. data/vendor/assets/fonts/font/ubuntumono-r-webfont.svg +242 -0
  57. data/vendor/assets/fonts/font/ubuntumono-r-webfont.ttf +0 -0
  58. data/vendor/assets/fonts/font/ubuntumono-r-webfont.woff +0 -0
  59. data/vendor/assets/fonts/font/ubuntumono-ri-webfont.eot +0 -0
  60. data/vendor/assets/fonts/font/ubuntumono-ri-webfont.svg +242 -0
  61. data/vendor/assets/fonts/font/ubuntumono-ri-webfont.ttf +0 -0
  62. data/vendor/assets/fonts/font/ubuntumono-ri-webfont.woff +0 -0
  63. data/vendor/assets/images/img/SAPOlogo.png +0 -0
  64. data/vendor/assets/images/img/favicon.ico +0 -0
  65. data/vendor/assets/images/img/home_bkg.png +0 -0
  66. data/vendor/assets/images/img/home_logo_IE.jpg +0 -0
  67. data/vendor/assets/images/img/icon_Sprite.png +0 -0
  68. data/vendor/assets/images/img/ink-favicon.ico +0 -0
  69. data/vendor/assets/images/img/logo_home.png +0 -0
  70. data/vendor/assets/images/img/shot_ink.png +0 -0
  71. data/vendor/assets/images/img/shot_intra.png +0 -0
  72. data/vendor/assets/images/img/shot_livebots.png +0 -0
  73. data/vendor/assets/images/img/shot_meo.png +0 -0
  74. data/vendor/assets/images/img/shot_musicbox.png +0 -0
  75. data/vendor/assets/images/img/shot_pessoa.png +0 -0
  76. data/vendor/assets/images/img/splash.1024x748.png +0 -0
  77. data/vendor/assets/images/img/splash.320x460.png +0 -0
  78. data/vendor/assets/images/img/splash.768x1004.png +0 -0
  79. data/vendor/assets/images/img/touch-icon.114.png +0 -0
  80. data/vendor/assets/images/img/touch-icon.16.png +0 -0
  81. data/vendor/assets/images/img/touch-icon.256.png +0 -0
  82. data/vendor/assets/images/img/touch-icon.57.png +0 -0
  83. data/vendor/assets/images/img/touch-icon.72.png +0 -0
  84. data/vendor/assets/javascripts/autoload.js +85 -0
  85. data/vendor/assets/javascripts/example.json +1174 -0
  86. data/vendor/assets/javascripts/holder.js +440 -0
  87. data/vendor/assets/javascripts/html5shiv-printshiv.js +496 -0
  88. data/vendor/assets/javascripts/html5shiv.js +298 -0
  89. data/vendor/assets/javascripts/ink-all.js +18015 -0
  90. data/vendor/assets/javascripts/ink-ui.js +7737 -0
  91. data/vendor/assets/javascripts/ink.aux.js +506 -0
  92. data/vendor/assets/javascripts/ink.close.js +54 -0
  93. data/vendor/assets/javascripts/ink.datepicker.js +1194 -0
  94. data/vendor/assets/javascripts/ink.datepicker.pt.js +32 -0
  95. data/vendor/assets/javascripts/ink.draggable.js +437 -0
  96. data/vendor/assets/javascripts/ink.droppable.js +193 -0
  97. data/vendor/assets/javascripts/ink.formvalidator.js +712 -0
  98. data/vendor/assets/javascripts/ink.gallery.js +757 -0
  99. data/vendor/assets/javascripts/ink.imagequery.js +259 -0
  100. data/vendor/assets/javascripts/ink.js +10278 -0
  101. data/vendor/assets/javascripts/ink.modal.js +628 -0
  102. data/vendor/assets/javascripts/ink.pagination.js +473 -0
  103. data/vendor/assets/javascripts/ink.progressbar.js +110 -0
  104. data/vendor/assets/javascripts/ink.smoothscroller.js +234 -0
  105. data/vendor/assets/javascripts/ink.sortablelist.js +338 -0
  106. data/vendor/assets/javascripts/ink.spy.js +123 -0
  107. data/vendor/assets/javascripts/ink.sticky.js +254 -0
  108. data/vendor/assets/javascripts/ink.table.js +621 -0
  109. data/vendor/assets/javascripts/ink.tabs.js +426 -0
  110. data/vendor/assets/javascripts/ink.toggle.js +218 -0
  111. data/vendor/assets/javascripts/ink.treeview.js +179 -0
  112. data/vendor/assets/javascripts/ink_ui.js +1 -0
  113. data/vendor/assets/javascripts/modernizr.js +815 -0
  114. data/vendor/assets/javascripts/prettify.js +28 -0
  115. data/vendor/assets/stylesheets/ink/_ink-ie7.css +1662 -0
  116. data/vendor/assets/stylesheets/ink/_ink.css +7496 -0
  117. data/vendor/assets/stylesheets/ink/ink_ui.scss.css +2 -0
  118. data/vendor/assets/stylesheets/ink_ui.scss.css +1 -0
  119. metadata +203 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @module Ink.UI.Spy_1
3
+ * @author inkdev AT sapo.pt
4
+ * @version 1
5
+ */
6
+ Ink.createModule('Ink.UI.Spy', '1', ['Ink.UI.Aux_1','Ink.Dom.Event_1','Ink.Dom.Css_1','Ink.Dom.Element_1','Ink.Dom.Selector_1','Ink.Util.Array_1'], function(Aux, Event, Css, Element, Selector, InkArray ) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * Spy is a component that 'spies' an element (or a group of elements) and when they leave the viewport (through the top),
11
+ * highlight an option - related to that element being spied - that resides in a menu, initially identified as target.
12
+ *
13
+ * @class Ink.UI.Spy
14
+ * @constructor
15
+ * @version 1
16
+ * @uses Ink.UI.Aux
17
+ * @uses Ink.Dom.Event
18
+ * @uses Ink.Dom.Css
19
+ * @uses Ink.Dom.Element
20
+ * @uses Ink.Dom.Selector
21
+ * @uses Ink.Util.Array
22
+ * @param {String|DOMElement} selector
23
+ * @param {Object} [options] Options
24
+ * @param {DOMElement|String} options.target Target menu on where the spy will highlight the right option.
25
+ * @example
26
+ * <script>
27
+ * Ink.requireModules( ['Ink.Dom.Selector_1','Ink.UI.Spy_1'], function( Selector, Spy ){
28
+ * var menuElement = Ink.s('#menu');
29
+ * var specialAnchorToSpy = Ink.s('#specialAnchor');
30
+ * var spyObj = new Spy( specialAnchorToSpy, {
31
+ * target: menuElement
32
+ * });
33
+ * });
34
+ * </script>
35
+ */
36
+ var Spy = function( selector, options ){
37
+
38
+ this._rootElement = Aux.elOrSelector(selector,'1st argument');
39
+
40
+ /**
41
+ * Setting default options and - if needed - overriding it with the data attributes
42
+ */
43
+ this._options = Ink.extendObj({
44
+ target: undefined
45
+ }, Element.data( this._rootElement ) );
46
+
47
+ /**
48
+ * In case options have been defined when creating the instance, they've precedence
49
+ */
50
+ this._options = Ink.extendObj(this._options,options || {});
51
+
52
+ this._options.target = Aux.elOrSelector( this._options.target, 'Target' );
53
+
54
+ this._scrollTimeout = null;
55
+ this._init();
56
+ };
57
+
58
+ Spy.prototype = {
59
+
60
+ /**
61
+ * Stores the spy elements
62
+ *
63
+ * @property _elements
64
+ * @type {Array}
65
+ * @readOnly
66
+ *
67
+ */
68
+ _elements: [],
69
+
70
+ /**
71
+ * Init function called by the constructor
72
+ *
73
+ * @method _init
74
+ * @private
75
+ */
76
+ _init: function(){
77
+ Event.observe( document, 'scroll', Ink.bindEvent(this._onScroll,this) );
78
+ this._elements.push(this._rootElement);
79
+ },
80
+
81
+ /**
82
+ * Scroll handler. Responsible for highlighting the right options of the target menu.
83
+ *
84
+ * @method _onScroll
85
+ * @private
86
+ */
87
+ _onScroll: function(){
88
+
89
+ var scrollHeight = Element.scrollHeight();
90
+ if( (scrollHeight < this._rootElement.offsetTop) ){
91
+ return;
92
+ } else {
93
+ for( var i = 0, total = this._elements.length; i < total; i++ ){
94
+ if( (this._elements[i].offsetTop <= scrollHeight) && (this._elements[i] !== this._rootElement) && (this._elements[i].offsetTop > this._rootElement.offsetTop) ){
95
+ return;
96
+ }
97
+ }
98
+ }
99
+
100
+ InkArray.each(
101
+ Selector.select(
102
+ 'a',
103
+ this._options.target
104
+ ), Ink.bind(function(item){
105
+
106
+ var comparisonValue = ( ("name" in this._rootElement) && this._rootElement.name ?
107
+ '#' + this._rootElement.name : '#' + this._rootElement.id
108
+ );
109
+
110
+ if( item.href.substr(item.href.indexOf('#')) === comparisonValue ){
111
+ Css.addClassName(Element.findUpwardsByTag(item,'li'),'active');
112
+ } else {
113
+ Css.removeClassName(Element.findUpwardsByTag(item,'li'),'active');
114
+ }
115
+ },this)
116
+ );
117
+ }
118
+
119
+ };
120
+
121
+ return Spy;
122
+
123
+ });
@@ -0,0 +1,254 @@
1
+ /**
2
+ * @module Ink.UI.Sticky_1
3
+ * @author inkdev AT sapo.pt
4
+ * @version 1
5
+ */
6
+ Ink.createModule('Ink.UI.Sticky', '1', ['Ink.UI.Aux_1','Ink.Dom.Event_1','Ink.Dom.Css_1','Ink.Dom.Element_1','Ink.Dom.Selector_1'], function(Aux, Event, Css, Element, Selector ) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * The Sticky component takes an element and transforms it's behavior in order to, when the user scrolls he sets its position
11
+ * to fixed and maintain it until the user scrolls back to the same place.
12
+ *
13
+ * @class Ink.UI.Sticky
14
+ * @constructor
15
+ * @version 1
16
+ * @uses Ink.UI.Aux
17
+ * @uses Ink.Dom.Event
18
+ * @uses Ink.Dom.Css
19
+ * @uses Ink.Dom.Element
20
+ * @uses Ink.Dom.Selector
21
+ * @param {String|DOMElement} selector
22
+ * @param {Object} [options] Options
23
+ * @param {Number} options.offsetBottom Number of pixels of distance from the bottomElement.
24
+ * @param {Number} options.offsetTop Number of pixels of distance from the topElement.
25
+ * @param {String} options.topElement CSS Selector that specifies a top element with which the component could collide.
26
+ * @param {String} options.bottomElement CSS Selector that specifies a bottom element with which the component could collide.
27
+ * @example
28
+ * <script>
29
+ * Ink.requireModules( ['Ink.Dom.Selector_1','Ink.UI.Sticky_1'], function( Selector, Sticky ){
30
+ * var menuElement = Ink.s('#menu');
31
+ * var stickyObj = new Sticky( menuElement );
32
+ * });
33
+ * </script>
34
+ */
35
+ var Sticky = function( selector, options ){
36
+
37
+ if( typeof selector !== 'object' && typeof selector !== 'string'){
38
+ throw '[Sticky] :: Invalid selector defined';
39
+ }
40
+
41
+ if( typeof selector === 'object' ){
42
+ this._rootElement = selector;
43
+ } else {
44
+ this._rootElement = Selector.select( selector );
45
+ if( this._rootElement.length <= 0) {
46
+ throw "[Sticky] :: Can't find any element with the specified selector";
47
+ }
48
+ this._rootElement = this._rootElement[0];
49
+ }
50
+
51
+ /**
52
+ * Setting default options and - if needed - overriding it with the data attributes
53
+ */
54
+ this._options = Ink.extendObj({
55
+ offsetBottom: 0,
56
+ offsetTop: 0,
57
+ topElement: undefined,
58
+ bottomElement: undefined
59
+ }, Element.data( this._rootElement ) );
60
+
61
+ /**
62
+ * In case options have been defined when creating the instance, they've precedence
63
+ */
64
+ this._options = Ink.extendObj(this._options,options || {});
65
+
66
+ if( typeof( this._options.topElement ) !== 'undefined' ){
67
+ this._options.topElement = Aux.elOrSelector( this._options.topElement, 'Top Element');
68
+ } else {
69
+ this._options.topElement = Aux.elOrSelector( 'body', 'Top Element');
70
+ }
71
+
72
+ if( typeof( this._options.bottomElement ) !== 'undefined' ){
73
+ this._options.bottomElement = Aux.elOrSelector( this._options.bottomElement, 'Bottom Element');
74
+ } else {
75
+ this._options.bottomElement = Aux.elOrSelector( 'body', 'Top Element');
76
+ }
77
+
78
+ this._computedStyle = window.getComputedStyle ? window.getComputedStyle(this._rootElement, null) : this._rootElement.currentStyle;
79
+ this._dims = {
80
+ height: this._computedStyle.height,
81
+ width: this._computedStyle.width
82
+ };
83
+ this._init();
84
+ };
85
+
86
+ Sticky.prototype = {
87
+
88
+ /**
89
+ * Init function called by the constructor
90
+ *
91
+ * @method _init
92
+ * @private
93
+ */
94
+ _init: function(){
95
+ Event.observe( document, 'scroll', Ink.bindEvent(this._onScroll,this) );
96
+ Event.observe( window, 'resize', Ink.bindEvent(this._onResize,this) );
97
+
98
+ this._calculateOriginalSizes();
99
+
100
+ this._calculateOffsets();
101
+
102
+ },
103
+
104
+ /**
105
+ * Scroll handler.
106
+ *
107
+ * @method _onScroll
108
+ * @private
109
+ */
110
+ _onScroll: function(){
111
+
112
+
113
+ var viewport = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
114
+
115
+ if(
116
+ ( ( (Element.elementWidth(this._rootElement)*100)/viewport.clientWidth ) > 90 ) ||
117
+ ( viewport.clientWidth<=649 )
118
+ ){
119
+ if( Element.hasAttribute(this._rootElement,'style') ){
120
+ this._rootElement.removeAttribute('style');
121
+ }
122
+ return;
123
+ }
124
+
125
+
126
+ if( this._scrollTimeout ){
127
+ clearTimeout(this._scrollTimeout);
128
+ }
129
+
130
+ this._scrollTimeout = setTimeout(Ink.bind(function(){
131
+
132
+ var scrollHeight = Element.scrollHeight();
133
+
134
+ if( Element.hasAttribute(this._rootElement,'style') ){
135
+ if( scrollHeight <= this._options.offsetTop){
136
+ this._rootElement.removeAttribute('style');
137
+ } else if( ((document.body.scrollHeight-(scrollHeight+parseInt(this._dims.height,10))) < this._options.offsetBottom) ){
138
+ this._rootElement.style.position = 'fixed';
139
+ this._rootElement.style.top = 'auto';
140
+ if( this._options.offsetBottom < parseInt(document.body.scrollHeight - (document.documentElement.clientHeight+scrollHeight),10) ){
141
+ this._rootElement.style.bottom = this._options.originalOffsetBottom + 'px';
142
+ } else {
143
+ this._rootElement.style.bottom = this._options.offsetBottom - parseInt(document.body.scrollHeight - (document.documentElement.clientHeight+scrollHeight),10) + 'px';
144
+ }
145
+ this._rootElement.style.width = this._options.originalWidth + 'px';
146
+ } else if( ((document.body.scrollHeight-(scrollHeight+parseInt(this._dims.height,10))) >= this._options.offsetBottom) ){
147
+ this._rootElement.style.position = 'fixed';
148
+ this._rootElement.style.bottom = 'auto';
149
+ this._rootElement.style.top = this._options.originalOffsetTop + 'px';
150
+ this._rootElement.style.width = this._options.originalWidth + 'px';
151
+ }
152
+ } else {
153
+ if(scrollHeight <= this._options.offsetTop ){
154
+ return;
155
+ }
156
+
157
+ this._rootElement.style.position = 'fixed';
158
+ this._rootElement.style.bottom = 'auto';
159
+ this._rootElement.style.top = this._options.offsetTop + 'px';
160
+ this._rootElement.style.width = this._options.originalWidth + 'px';
161
+ }
162
+
163
+ this._scrollTimeout = undefined;
164
+ },this), 0);
165
+ },
166
+
167
+ /**
168
+ * Resize handler
169
+ *
170
+ * @method _onResize
171
+ * @private
172
+ */
173
+ _onResize: function(){
174
+
175
+ if( this._resizeTimeout ){
176
+ clearTimeout(this._resizeTimeout);
177
+ }
178
+
179
+ this._resizeTimeout = setTimeout(Ink.bind(function(){
180
+ this._rootElement.removeAttribute('style');
181
+ this._calculateOriginalSizes();
182
+ this._calculateOffsets();
183
+ }, this),0);
184
+
185
+ },
186
+
187
+ /**
188
+ * On each resizing (and in the beginning) the component recalculates the offsets, since
189
+ * the top and bottom element heights might have changed.
190
+ *
191
+ * @method _calculateOffsets
192
+ * @private
193
+ */
194
+ _calculateOffsets: function(){
195
+
196
+ /**
197
+ * Calculating the offset top
198
+ */
199
+ if( typeof this._options.topElement !== 'undefined' ){
200
+
201
+
202
+ if( this._options.topElement.nodeName.toLowerCase() !== 'body' ){
203
+ var
204
+ topElementHeight = Element.elementHeight( this._options.topElement ),
205
+ topElementTop = Element.elementTop( this._options.topElement )
206
+ ;
207
+
208
+ this._options.offsetTop = ( parseInt(topElementHeight,10) + parseInt(topElementTop,10) ) + parseInt(this._options.originalOffsetTop,10);
209
+ } else {
210
+ this._options.offsetTop = parseInt(this._options.originalOffsetTop,10);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Calculating the offset bottom
216
+ */
217
+ if( typeof this._options.bottomElement !== 'undefined' ){
218
+
219
+ if( this._options.bottomElement.nodeName.toLowerCase() !== 'body' ){
220
+ var
221
+ bottomElementHeight = Element.elementHeight(this._options.bottomElement)
222
+ ;
223
+ this._options.offsetBottom = parseInt(bottomElementHeight,10) + parseInt(this._options.originalOffsetBottom,10);
224
+ } else {
225
+ this._options.offsetBottom = parseInt(this._options.originalOffsetBottom,10);
226
+ }
227
+ }
228
+
229
+ this._onScroll();
230
+
231
+ },
232
+
233
+ /**
234
+ * Function to calculate the 'original size' of the element.
235
+ * It's used in the begining (_init method) and when a scroll happens
236
+ *
237
+ * @method _calculateOriginalSizes
238
+ * @private
239
+ */
240
+ _calculateOriginalSizes: function(){
241
+ this._options.originalOffsetTop = parseInt(this._options.offsetTop,10);
242
+ this._options.originalOffsetBottom = parseInt(this._options.offsetBottom,10);
243
+ this._options.originalTop = parseInt(this._rootElement.offsetTop,10);
244
+ if(isNaN(this._options.originalWidth = parseInt(this._dims.width,10))) {
245
+ this._options.originalWidth = 0;
246
+ }
247
+ this._options.originalWidth = parseInt(this._computedStyle.width,10);
248
+ }
249
+
250
+ };
251
+
252
+ return Sticky;
253
+
254
+ });
@@ -0,0 +1,621 @@
1
+ /**
2
+ * @module Ink.UI.Table_1
3
+ * @author inkdev AT sapo.pt
4
+ * @version 1
5
+ */
6
+ Ink.createModule('Ink.UI.Table', '1', ['Ink.Net.Ajax_1','Ink.UI.Aux_1','Ink.Dom.Event_1','Ink.Dom.Css_1','Ink.Dom.Element_1','Ink.Dom.Selector_1','Ink.Util.Array_1','Ink.Util.String_1'], function(Ajax, Aux, Event, Css, Element, Selector, InkArray, InkString ) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * The Table component transforms the native/DOM table element into a
11
+ * sortable, paginated component.
12
+ *
13
+ * @class Ink.UI.Table
14
+ * @constructor
15
+ * @version 1
16
+ * @uses Ink.UI.Aux
17
+ * @uses Ink.Dom.Event
18
+ * @uses Ink.Dom.Css
19
+ * @uses Ink.Dom.Element
20
+ * @uses Ink.Dom.Selector
21
+ * @uses Ink.Util.Array
22
+ * @uses Ink.UI.Pagination
23
+ * @param {String|DOMElement} selector
24
+ * @param {Object} [options] Options
25
+ * @param {Number} options.pageSize Number of rows per page.
26
+ * @param {String} options.endpoint Endpoint to get the records via AJAX
27
+ * @example
28
+ * <table class="ink-table alternating" data-page-size="6">
29
+ * <thead>
30
+ * <tr>
31
+ * <th data-sortable="true" width="75%">Pepper</th>
32
+ * <th data-sortable="true" width="25%">Scoville Rating</th>
33
+ * </tr>
34
+ * </thead>
35
+ * <tbody>
36
+ * <tr>
37
+ * <td>Trinidad Moruga Scorpion</td>
38
+ * <td>1500000</td>
39
+ * </tr>
40
+ * <tr>
41
+ * <td>Bhut Jolokia</td>
42
+ * <td>1000000</td>
43
+ * </tr>
44
+ * <tr>
45
+ * <td>Naga Viper</td>
46
+ * <td>1463700</td>
47
+ * </tr>
48
+ * <tr>
49
+ * <td>Red Savina Habanero</td>
50
+ * <td>580000</td>
51
+ * </tr>
52
+ * <tr>
53
+ * <td>Habanero</td>
54
+ * <td>350000</td>
55
+ * </tr>
56
+ * <tr>
57
+ * <td>Scotch Bonnet</td>
58
+ * <td>180000</td>
59
+ * </tr>
60
+ * <tr>
61
+ * <td>Malagueta</td>
62
+ * <td>50000</td>
63
+ * </tr>
64
+ * <tr>
65
+ * <td>Tabasco</td>
66
+ * <td>35000</td>
67
+ * </tr>
68
+ * <tr>
69
+ * <td>Serrano Chili</td>
70
+ * <td>27000</td>
71
+ * </tr>
72
+ * <tr>
73
+ * <td>Jalapeño</td>
74
+ * <td>8000</td>
75
+ * </tr>
76
+ * <tr>
77
+ * <td>Poblano</td>
78
+ * <td>1500</td>
79
+ * </tr>
80
+ * <tr>
81
+ * <td>Peperoncino</td>
82
+ * <td>500</td>
83
+ * </tr>
84
+ * </tbody>
85
+ * </table>
86
+ * <nav class="ink-navigation"><ul class="pagination"></ul></nav>
87
+ * <script>
88
+ * Ink.requireModules( ['Ink.Dom.Selector_1','Ink.UI.Table_1'], function( Selector, Table ){
89
+ * var tableElement = Ink.s('.ink-table');
90
+ * var tableObj = new Table( tableElement );
91
+ * });
92
+ * </script>
93
+ */
94
+ var Table = function( selector, options ){
95
+
96
+ /**
97
+ * Get the root element
98
+ */
99
+ this._rootElement = Aux.elOrSelector(selector, '1st argument');
100
+
101
+ if( this._rootElement.nodeName.toLowerCase() !== 'table' ){
102
+ throw '[Ink.UI.Table] :: The element is not a table';
103
+ }
104
+
105
+ this._options = Ink.extendObj({
106
+ pageSize: undefined,
107
+ endpoint: undefined,
108
+ loadMode: 'full',
109
+ allowResetSorting: false,
110
+ visibleFields: undefined
111
+ },Element.data(this._rootElement));
112
+
113
+ this._options = Ink.extendObj( this._options, options || {});
114
+
115
+ /**
116
+ * Checking if it's in markup mode or endpoint mode
117
+ */
118
+ this._markupMode = ( typeof this._options.endpoint === 'undefined' );
119
+
120
+ if( !!this._options.visibleFields ){
121
+ this._options.visibleFields = this._options.visibleFields.split(',');
122
+ }
123
+
124
+ /**
125
+ * Initializing variables
126
+ */
127
+ this._handlers = {
128
+ click: Ink.bindEvent(this._onClick,this)
129
+ };
130
+ this._originalFields = [];
131
+ this._sortableFields = {};
132
+ this._originalData = this._data = [];
133
+ this._headers = [];
134
+ this._pagination = null;
135
+ this._totalRows = 0;
136
+
137
+ this._init();
138
+ };
139
+
140
+ Table.prototype = {
141
+
142
+ /**
143
+ * Init function called by the constructor
144
+ *
145
+ * @method _init
146
+ * @private
147
+ */
148
+ _init: function(){
149
+
150
+ /**
151
+ * If not is in markup mode, we have to do the initial request
152
+ * to get the first data and the headers
153
+ */
154
+ if( !this._markupMode ){
155
+ this._getData( this._options.endpoint, true );
156
+ } else{
157
+ this._setHeadersHandlers();
158
+
159
+ /**
160
+ * Getting the table's data
161
+ */
162
+ InkArray.each(Selector.select('tbody tr',this._rootElement),Ink.bind(function(tr){
163
+ this._data.push(tr);
164
+ },this));
165
+ this._originalData = this._data.slice(0);
166
+
167
+ this._totalRows = this._data.length;
168
+
169
+ /**
170
+ * Set pagination if defined
171
+ *
172
+ */
173
+ if( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') ){
174
+ /**
175
+ * Applying the pagination
176
+ */
177
+ this._pagination = this._rootElement.nextSibling;
178
+ while(this._pagination.nodeType !== 1){
179
+ this._pagination = this._pagination.nextSibling;
180
+ }
181
+
182
+ if( this._pagination.nodeName.toLowerCase() !== 'nav' ){
183
+ throw '[Ink.UI.Table] :: Missing the pagination markup or is mis-positioned';
184
+ }
185
+
186
+ var Pagination = Ink.getModule('Ink.UI.Pagination',1);
187
+
188
+ this._pagination = new Pagination( this._pagination, {
189
+ size: Math.ceil(this._totalRows/this._options.pageSize),
190
+ onChange: Ink.bind(function( pagingObj ){
191
+ this._paginate( (pagingObj._current+1) );
192
+ },this)
193
+ });
194
+
195
+ this._paginate(1);
196
+ }
197
+ }
198
+
199
+ },
200
+
201
+ /**
202
+ * Click handler. This will mainly handle the sorting (when you click in the headers)
203
+ *
204
+ * @method _onClick
205
+ * @param {Event} event Event obj
206
+ * @private
207
+ */
208
+ _onClick: function( event ){
209
+ Event.stop(event);
210
+ var
211
+ tgtEl = Event.element(event),
212
+ dataset = Element.data(tgtEl),
213
+ index,i,
214
+ paginated = ( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') )
215
+ ;
216
+ if( (tgtEl.nodeName.toLowerCase() !== 'th') || ( !("sortable" in dataset) || (dataset.sortable.toString() !== 'true') ) ){
217
+ return;
218
+ }
219
+
220
+ index = -1;
221
+ if( InkArray.inArray( tgtEl,this._headers ) ){
222
+ for( i=0; i<this._headers.length; i++ ){
223
+ if( this._headers[i] === tgtEl ){
224
+ index = i;
225
+ break;
226
+ }
227
+ }
228
+ }
229
+
230
+ if( !this._markupMode && paginated ){
231
+
232
+ for( var prop in this._sortableFields ){
233
+ if( prop !== ('col_' + index) ){
234
+ this._sortableFields[prop] = 'none';
235
+ this._headers[prop.replace('col_','')].innerHTML = InkString.stripTags(this._headers[prop.replace('col_','')].innerHTML);
236
+ }
237
+ }
238
+
239
+ if( this._sortableFields['col_'+index] === 'asc' )
240
+ {
241
+ this._sortableFields['col_'+index] = 'desc';
242
+ this._headers[index].innerHTML = InkString.stripTags(this._headers[index].innerHTML) + '<i class="icon-caret-down"></i>';
243
+ } else {
244
+ this._sortableFields['col_'+index] = 'asc';
245
+ this._headers[index].innerHTML = InkString.stripTags(this._headers[index].innerHTML) + '<i class="icon-caret-up"></i>';
246
+
247
+ }
248
+
249
+ this._pagination.setCurrent(this._pagination._current);
250
+
251
+ } else {
252
+
253
+ if( index === -1){
254
+ return;
255
+ }
256
+
257
+ if( (this._sortableFields['col_'+index] === 'desc') && (this._options.allowResetSorting && (this._options.allowResetSorting.toString() === 'true')) )
258
+ {
259
+ this._headers[index].innerHTML = InkString.stripTags(this._headers[index].innerHTML);
260
+ this._sortableFields['col_'+index] = 'none';
261
+
262
+ // if( !found ){
263
+ this._data = this._originalData.slice(0);
264
+ // }
265
+ } else {
266
+
267
+ for( var prop in this._sortableFields ){
268
+ if( prop !== ('col_' + index) ){
269
+ this._sortableFields[prop] = 'none';
270
+ this._headers[prop.replace('col_','')].innerHTML = InkString.stripTags(this._headers[prop.replace('col_','')].innerHTML);
271
+ }
272
+ }
273
+
274
+ this._sort(index);
275
+
276
+ if( this._sortableFields['col_'+index] === 'asc' )
277
+ {
278
+ this._data.reverse();
279
+ this._sortableFields['col_'+index] = 'desc';
280
+ this._headers[index].innerHTML = InkString.stripTags(this._headers[index].innerHTML) + '<i class="icon-caret-down"></i>';
281
+ } else {
282
+ this._sortableFields['col_'+index] = 'asc';
283
+ this._headers[index].innerHTML = InkString.stripTags(this._headers[index].innerHTML) + '<i class="icon-caret-up"></i>';
284
+
285
+ }
286
+ }
287
+
288
+
289
+ var tbody = Selector.select('tbody',this._rootElement)[0];
290
+ Aux.cleanChildren(tbody);
291
+ InkArray.each(this._data,function(item){
292
+ tbody.appendChild(item);
293
+ });
294
+
295
+ this._pagination.setCurrent(0);
296
+ this._paginate(1);
297
+ }
298
+ },
299
+
300
+ /**
301
+ * Applies and/or changes the CSS classes in order to show the right columns
302
+ *
303
+ * @method _paginate
304
+ * @param {Number} page Current page
305
+ * @private
306
+ */
307
+ _paginate: function( page ){
308
+ InkArray.each(this._data,Ink.bind(function(item, index){
309
+ if( (index >= ((page-1)*parseInt(this._options.pageSize,10))) && (index < (((page-1)*parseInt(this._options.pageSize,10))+parseInt(this._options.pageSize,10)) ) ){
310
+ Css.removeClassName(item,'hide-all');
311
+ } else {
312
+ Css.addClassName(item,'hide-all');
313
+ }
314
+ },this));
315
+ },
316
+
317
+ /**
318
+ * Sorts by a specific column.
319
+ *
320
+ * @method _sort
321
+ * @param {Number} index Column number (starting at 0)
322
+ * @private
323
+ */
324
+ _sort: function( index ){
325
+ this._data.sort(Ink.bind(function(a,b){
326
+ var
327
+ aValue = Selector.select('td',a)[index].innerText,
328
+ bValue = Selector.select('td',b)[index].innerText
329
+ ;
330
+
331
+ var regex = new RegExp(/\d/g);
332
+ if( !isNaN(aValue) && regex.test(aValue) ){
333
+ aValue = parseInt(aValue,10);
334
+ } else if( !isNaN(aValue) ){
335
+ aValue = parseFloat(aValue);
336
+ }
337
+
338
+ if( !isNaN(bValue) && regex.test(bValue) ){
339
+ bValue = parseInt(bValue,10);
340
+ } else if( !isNaN(bValue) ){
341
+ bValue = parseFloat(bValue);
342
+ }
343
+
344
+ if( aValue === bValue ){
345
+ return 0;
346
+ } else {
347
+ return ( ( aValue>bValue ) ? 1 : -1 );
348
+ }
349
+ },this));
350
+ },
351
+
352
+ /**
353
+ * Assembles the headers markup
354
+ *
355
+ * @method _setHeaders
356
+ * @param {Object} headers Key-value object that contains the fields as keys, their configuration (label and sorting ability) as value
357
+ * @private
358
+ */
359
+ _setHeaders: function( headers, rows ){
360
+ var
361
+ field, header,
362
+ thead, tr, th,
363
+ index = 0
364
+ ;
365
+
366
+ if( (thead = Selector.select('thead',this._rootElement)).length === 0 ){
367
+ thead = this._rootElement.createTHead();
368
+ tr = thead.insertRow(0);
369
+
370
+ for( field in headers ){
371
+
372
+ if( !!this._options.visibleFields && (this._options.visibleFields.indexOf(field) === -1) ){
373
+ continue;
374
+ }
375
+
376
+ // th = tr.insertCell(index++);
377
+ th = document.createElement('th');
378
+ header = headers[field];
379
+
380
+ if( ("sortable" in header) && (header.sortable.toString() === 'true') ){
381
+ th.setAttribute('data-sortable','true');
382
+ }
383
+
384
+ if( ("label" in header) ){
385
+ th.innerText = header.label;
386
+ }
387
+
388
+ this._originalFields.push(field);
389
+ tr.appendChild(th);
390
+ }
391
+ } else {
392
+ var firstLine = rows[0];
393
+
394
+ for( field in firstLine ){
395
+ if( !!this._options.visibleFields && (this._options.visibleFields.indexOf(field) === -1) ){
396
+ continue;
397
+ }
398
+
399
+ this._originalFields.push(field);
400
+ }
401
+ }
402
+ },
403
+
404
+ /**
405
+ * Method that sets the handlers for the headers
406
+ *
407
+ * @method _setHeadersHandlers
408
+ * @private
409
+ */
410
+ _setHeadersHandlers: function(){
411
+
412
+ /**
413
+ * Setting the sortable columns and its event listeners
414
+ */
415
+ Event.observe(Selector.select('thead',this._rootElement)[0],'click',this._handlers.click);
416
+ this._headers = Selector.select('thead tr th',this._rootElement);
417
+ InkArray.each(this._headers,Ink.bind(function(item, index){
418
+ var dataset = Element.data( item );
419
+ if( ('sortable' in dataset) && (dataset.sortable.toString() === 'true') ){
420
+ this._sortableFields['col_' + index] = 'none';
421
+ }
422
+ },this));
423
+
424
+ },
425
+
426
+ /**
427
+ * This method gets the rows from AJAX and places them as <tr> and <td>
428
+ *
429
+ * @method _setData
430
+ * @param {Object} rows Array of objects with the data to be showed
431
+ * @private
432
+ */
433
+ _setData: function( rows ){
434
+
435
+ var
436
+ field,
437
+ tbody, tr, td,
438
+ trIndex,
439
+ tdIndex
440
+ ;
441
+
442
+ tbody = Selector.select('tbody',this._rootElement);
443
+ if( tbody.length === 0){
444
+ tbody = document.createElement('tbody');
445
+ this._rootElement.appendChild( tbody );
446
+ } else {
447
+ tbody = tbody[0];
448
+ tbody.innerHTML = '';
449
+ }
450
+
451
+ this._data = [];
452
+
453
+
454
+ for( trIndex in rows ){
455
+ tr = document.createElement('tr');
456
+ tbody.appendChild( tr );
457
+ tdIndex = 0;
458
+ for( field in rows[trIndex] ){
459
+
460
+ if( !!this._options.visibleFields && (this._options.visibleFields.indexOf(field) === -1) ){
461
+ continue;
462
+ }
463
+
464
+ td = tr.insertCell(tdIndex++);
465
+ td.innerHTML = rows[trIndex][field];
466
+ }
467
+ this._data.push(tr);
468
+ }
469
+
470
+ this._originalData = this._data.slice(0);
471
+ },
472
+
473
+ /**
474
+ * Sets the endpoint. Useful for changing the endpoint in runtime.
475
+ *
476
+ * @method _setEndpoint
477
+ * @param {String} endpoint New endpoint
478
+ */
479
+ setEndpoint: function( endpoint, currentPage ){
480
+ if( !this._markupMode ){
481
+ this._options.endpoint = endpoint;
482
+ this._pagination.setCurrent( (!!currentPage) ? parseInt(currentPage,10) : 0 );
483
+ }
484
+ },
485
+
486
+ /**
487
+ * Checks if it needs the pagination and creates the necessary markup to have pagination
488
+ *
489
+ * @method _setPagination
490
+ * @private
491
+ */
492
+ _setPagination: function(){
493
+ var paginated = ( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') );
494
+ /**
495
+ * Set pagination if defined
496
+ */
497
+ if( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') ){
498
+ /**
499
+ * Applying the pagination
500
+ */
501
+ if( !this._pagination ){
502
+ this._pagination = document.createElement('nav');
503
+ this._pagination.className = 'ink-navigation';
504
+ this._rootElement.parentNode.insertBefore(this._pagination,this._rootElement.nextSibling);
505
+ this._pagination.appendChild( document.createElement('ul') ).className = 'pagination';
506
+
507
+ var Pagination = Ink.getModule('Ink.UI.Pagination',1);
508
+
509
+ this._pagination = new Pagination( this._pagination, {
510
+ size: Math.ceil(this._totalRows/this._options.pageSize),
511
+ onChange: Ink.bind(function( ){
512
+ this._getData( this._options.endpoint );
513
+ },this)
514
+ });
515
+ }
516
+ }
517
+ },
518
+
519
+ /**
520
+ * Method to choose which is the best way to get the data based on the endpoint:
521
+ * - AJAX
522
+ * - JSONP
523
+ *
524
+ * @method _getData
525
+ * @param {String} endpoint Valid endpoint
526
+ * @param {Boolean} [firstRequest] If true, will make the request set the headers onSuccess
527
+ * @private
528
+ */
529
+ _getData: function( endpoint ){
530
+
531
+ Ink.requireModules(['Ink.Util.Url_1'],Ink.bind(function( InkURL ){
532
+
533
+ var
534
+ parsedURL = InkURL.parseUrl( endpoint ),
535
+ paginated = ( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') ),
536
+ pageNum = ((!!this._pagination) ? this._pagination._current+1 : 1)
537
+ ;
538
+
539
+ if( parsedURL.query ){
540
+ parsedURL.query = parsedURL.query.split("&");
541
+ } else {
542
+ parsedURL.query = [];
543
+ }
544
+
545
+ if( !paginated ){
546
+ this._getDataViaAjax( endpoint );
547
+ } else {
548
+
549
+ parsedURL.query.push( 'rows_per_page=' + this._options.pageSize );
550
+ parsedURL.query.push( 'page=' + pageNum );
551
+
552
+ var sortStr = '';
553
+ for( var index in this._sortableFields ){
554
+ if( this._sortableFields[index] !== 'none' ){
555
+ parsedURL.query.push('sortField=' + this._originalFields[parseInt(index.replace('col_',''),10)]);
556
+ parsedURL.query.push('sortOrder=' + this._sortableFields[index]);
557
+ break;
558
+ }
559
+ }
560
+
561
+ this._getDataViaAjax( endpoint + '?' + parsedURL.query.join('&') );
562
+ }
563
+
564
+ },this));
565
+
566
+ },
567
+
568
+ /**
569
+ * Gets the data via AJAX and triggers the changes in the
570
+ *
571
+ * @param {[type]} endpoint [description]
572
+ * @param {[type]} firstRequest [description]
573
+ * @return {[type]} [description]
574
+ */
575
+ _getDataViaAjax: function( endpoint ){
576
+
577
+ var paginated = ( ("pageSize" in this._options) && (typeof this._options.pageSize !== 'undefined') );
578
+
579
+ new Ajax( endpoint, {
580
+ method: 'GET',
581
+ contentType: 'application/json',
582
+ sanitizeJSON: true,
583
+ onSuccess: Ink.bind(function( response ){
584
+ if( response.status === 200 ){
585
+
586
+ var jsonResponse = JSON.parse( response.responseText );
587
+
588
+ if( this._headers.length === 0 ){
589
+ this._setHeaders( jsonResponse.headers, jsonResponse.rows );
590
+ this._setHeadersHandlers();
591
+ }
592
+
593
+ this._setData( jsonResponse.rows );
594
+
595
+ if( paginated ){
596
+ if( !!this._totalRows && (parseInt(jsonResponse.totalRows,10) !== parseInt(this._totalRows,10)) ){
597
+ this._totalRows = jsonResponse.totalRows;
598
+ this._pagination.setSize( Math.ceil(this._totalRows/this._options.pageSize) );
599
+ } else {
600
+ this._totalRows = jsonResponse.totalRows;
601
+ }
602
+ } else {
603
+ if( !!this._totalRows && (jsonResponse.rows.length !== parseInt(this._totalRows,10)) ){
604
+ this._totalRows = jsonResponse.rows.length;
605
+ this._pagination.setSize( Math.ceil(this._totalRows/this._options.pageSize) );
606
+ } else {
607
+ this._totalRows = jsonResponse.rows.length;
608
+ }
609
+ }
610
+
611
+ this._setPagination( );
612
+ }
613
+
614
+ },this)
615
+ } );
616
+ }
617
+ };
618
+
619
+ return Table;
620
+
621
+ });