postrest 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (234) hide show
  1. checksums.yaml +4 -4
  2. data/lib/app.rb +1 -1
  3. data/public/index.htm +116 -0
  4. data/public/node_modules/pivottable/CONTRIBUTING.md +19 -0
  5. data/public/node_modules/pivottable/LICENSE.md +9 -0
  6. data/public/node_modules/pivottable/ReadMe.md +155 -0
  7. data/public/node_modules/pivottable/bower.json +31 -0
  8. data/public/node_modules/pivottable/c3_renderers.coffee +155 -0
  9. data/public/node_modules/pivottable/d3_renderers.coffee +73 -0
  10. data/public/node_modules/pivottable/dist/c3_renderers.js +256 -0
  11. data/public/node_modules/pivottable/dist/c3_renderers.js.map +1 -0
  12. data/public/node_modules/pivottable/dist/c3_renderers.min.js +2 -0
  13. data/public/node_modules/pivottable/dist/c3_renderers.min.js.map +1 -0
  14. data/public/node_modules/pivottable/dist/d3_renderers.js +105 -0
  15. data/public/node_modules/pivottable/dist/d3_renderers.js.map +1 -0
  16. data/public/node_modules/pivottable/dist/d3_renderers.min.js +2 -0
  17. data/public/node_modules/pivottable/dist/d3_renderers.min.js.map +1 -0
  18. data/public/node_modules/pivottable/dist/export_renderers.js +80 -0
  19. data/public/node_modules/pivottable/dist/export_renderers.js.map +1 -0
  20. data/public/node_modules/pivottable/dist/export_renderers.min.js +2 -0
  21. data/public/node_modules/pivottable/dist/export_renderers.min.js.map +1 -0
  22. data/public/node_modules/pivottable/dist/gchart_renderers.js +183 -0
  23. data/public/node_modules/pivottable/dist/gchart_renderers.js.map +1 -0
  24. data/public/node_modules/pivottable/dist/gchart_renderers.min.js +2 -0
  25. data/public/node_modules/pivottable/dist/gchart_renderers.min.js.map +1 -0
  26. data/public/node_modules/pivottable/dist/pivot.css +97 -0
  27. data/public/node_modules/pivottable/dist/pivot.es.js +78 -0
  28. data/public/node_modules/pivottable/dist/pivot.es.js.map +1 -0
  29. data/public/node_modules/pivottable/dist/pivot.es.min.js +2 -0
  30. data/public/node_modules/pivottable/dist/pivot.es.min.js.map +1 -0
  31. data/public/node_modules/pivottable/dist/pivot.fr.js +78 -0
  32. data/public/node_modules/pivottable/dist/pivot.fr.js.map +1 -0
  33. data/public/node_modules/pivottable/dist/pivot.fr.min.js +2 -0
  34. data/public/node_modules/pivottable/dist/pivot.fr.min.js.map +1 -0
  35. data/public/node_modules/pivottable/dist/pivot.js +1511 -0
  36. data/public/node_modules/pivottable/dist/pivot.js.map +1 -0
  37. data/public/node_modules/pivottable/dist/pivot.min.css +1 -0
  38. data/public/node_modules/pivottable/dist/pivot.min.js +2 -0
  39. data/public/node_modules/pivottable/dist/pivot.min.js.map +1 -0
  40. data/public/node_modules/pivottable/dist/pivot.nl.js +78 -0
  41. data/public/node_modules/pivottable/dist/pivot.nl.js.map +1 -0
  42. data/public/node_modules/pivottable/dist/pivot.nl.min.js +2 -0
  43. data/public/node_modules/pivottable/dist/pivot.nl.min.js.map +1 -0
  44. data/public/node_modules/pivottable/dist/pivot.pt.js +104 -0
  45. data/public/node_modules/pivottable/dist/pivot.pt.js.map +1 -0
  46. data/public/node_modules/pivottable/dist/pivot.pt.min.js +2 -0
  47. data/public/node_modules/pivottable/dist/pivot.pt.min.js.map +1 -0
  48. data/public/node_modules/pivottable/dist/pivot.ru.js +78 -0
  49. data/public/node_modules/pivottable/dist/pivot.ru.js.map +1 -0
  50. data/public/node_modules/pivottable/dist/pivot.ru.min.js +2 -0
  51. data/public/node_modules/pivottable/dist/pivot.ru.min.js.map +1 -0
  52. data/public/node_modules/pivottable/dist/pivot.tr.js +104 -0
  53. data/public/node_modules/pivottable/dist/pivot.tr.js.map +1 -0
  54. data/public/node_modules/pivottable/dist/pivot.tr.min.js +2 -0
  55. data/public/node_modules/pivottable/dist/pivot.tr.min.js.map +1 -0
  56. data/public/node_modules/pivottable/dist/tips_data.min.js +247 -0
  57. data/public/node_modules/pivottable/examples/c3.html +61 -0
  58. data/public/node_modules/pivottable/examples/d3.html +49 -0
  59. data/public/node_modules/pivottable/examples/fully_loaded.html +80 -0
  60. data/public/node_modules/pivottable/examples/gchart.html +53 -0
  61. data/public/node_modules/pivottable/examples/gh-fork-ribbon.css +140 -0
  62. data/public/node_modules/pivottable/examples/gh-fork-ribbon.ie.css +78 -0
  63. data/public/node_modules/pivottable/examples/index.html +85 -0
  64. data/public/node_modules/pivottable/examples/local.html +74 -0
  65. data/public/node_modules/pivottable/examples/montreal_2014.csv +366 -0
  66. data/public/node_modules/pivottable/examples/montreal_2014.html +106 -0
  67. data/public/node_modules/pivottable/examples/mps.csv +309 -0
  68. data/public/node_modules/pivottable/examples/mps.html +45 -0
  69. data/public/node_modules/pivottable/examples/mps.json +1 -0
  70. data/public/node_modules/pivottable/examples/mps_agg.html +44 -0
  71. data/public/node_modules/pivottable/examples/mps_csv.html +42 -0
  72. data/public/node_modules/pivottable/examples/mps_export.html +44 -0
  73. data/public/node_modules/pivottable/examples/mps_fr.html +40 -0
  74. data/public/node_modules/pivottable/examples/mps_prepop.html +42 -0
  75. data/public/node_modules/pivottable/examples/onrefresh.html +52 -0
  76. data/public/node_modules/pivottable/examples/rcsvs.html +87 -0
  77. data/public/node_modules/pivottable/examples/scatter.html +53 -0
  78. data/public/node_modules/pivottable/examples/show_code.js +17 -0
  79. data/public/node_modules/pivottable/examples/simple.html +41 -0
  80. data/public/node_modules/pivottable/examples/simple_agg.html +51 -0
  81. data/public/node_modules/pivottable/examples/simple_function.html +57 -0
  82. data/public/node_modules/pivottable/examples/simple_ui.html +42 -0
  83. data/public/node_modules/pivottable/examples/simple_ui_from_table.html +60 -0
  84. data/public/node_modules/pivottable/export_renderers.coffee +57 -0
  85. data/public/node_modules/pivottable/gchart_renderers.coffee +111 -0
  86. data/public/node_modules/pivottable/gulpFile.js +95 -0
  87. data/public/node_modules/pivottable/images/animation.gif +0 -0
  88. data/public/node_modules/pivottable/images/filters.png +0 -0
  89. data/public/node_modules/pivottable/images/gender_age_bins.png +0 -0
  90. data/public/node_modules/pivottable/images/gender_imbalance.png +0 -0
  91. data/public/node_modules/pivottable/images/gender_imbalance_instructions.png +0 -0
  92. data/public/node_modules/pivottable/images/initial_state.png +0 -0
  93. data/public/node_modules/pivottable/images/province_x_party_heatmap.png +0 -0
  94. data/public/node_modules/pivottable/images/province_x_party_heatmap_instructions.png +0 -0
  95. data/public/node_modules/pivottable/images/simple.png +0 -0
  96. data/public/node_modules/pivottable/images/simple_ui.png +0 -0
  97. data/public/node_modules/pivottable/images/whats_what.png +0 -0
  98. data/public/node_modules/pivottable/index.html +14 -0
  99. data/public/node_modules/pivottable/node_modules/jquery/AUTHORS.txt +278 -0
  100. data/public/node_modules/pivottable/node_modules/jquery/LICENSE.txt +36 -0
  101. data/public/node_modules/pivottable/node_modules/jquery/README.md +65 -0
  102. data/public/node_modules/pivottable/node_modules/jquery/bower.json +14 -0
  103. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.js +9842 -0
  104. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.min.js +4 -0
  105. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.min.map +1 -0
  106. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.slim.js +7877 -0
  107. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.slim.min.js +4 -0
  108. data/public/node_modules/pivottable/node_modules/jquery/dist/jquery.slim.min.map +1 -0
  109. data/public/node_modules/pivottable/node_modules/jquery/package.json +122 -0
  110. data/public/node_modules/pivottable/node_modules/jquery/sizzle/LICENSE.txt +36 -0
  111. data/public/node_modules/pivottable/node_modules/jquery/sizzle/dist/sizzle.js +2143 -0
  112. data/public/node_modules/pivottable/node_modules/jquery/sizzle/dist/sizzle.min.js +3 -0
  113. data/public/node_modules/pivottable/node_modules/jquery/sizzle/dist/sizzle.min.map +1 -0
  114. data/public/node_modules/pivottable/node_modules/jquery/src/ajax.js +845 -0
  115. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/jsonp.js +100 -0
  116. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/load.js +83 -0
  117. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/parseJSON.js +13 -0
  118. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/parseXML.js +27 -0
  119. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/script.js +68 -0
  120. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/var/location.js +3 -0
  121. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/var/nonce.js +5 -0
  122. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/var/rquery.js +3 -0
  123. data/public/node_modules/pivottable/node_modules/jquery/src/ajax/xhr.js +167 -0
  124. data/public/node_modules/pivottable/node_modules/jquery/src/attributes.js +11 -0
  125. data/public/node_modules/pivottable/node_modules/jquery/src/attributes/attr.js +142 -0
  126. data/public/node_modules/pivottable/node_modules/jquery/src/attributes/classes.js +177 -0
  127. data/public/node_modules/pivottable/node_modules/jquery/src/attributes/prop.js +125 -0
  128. data/public/node_modules/pivottable/node_modules/jquery/src/attributes/support.js +36 -0
  129. data/public/node_modules/pivottable/node_modules/jquery/src/attributes/val.js +177 -0
  130. data/public/node_modules/pivottable/node_modules/jquery/src/callbacks.js +232 -0
  131. data/public/node_modules/pivottable/node_modules/jquery/src/core.js +494 -0
  132. data/public/node_modules/pivottable/node_modules/jquery/src/core/DOMEval.js +14 -0
  133. data/public/node_modules/pivottable/node_modules/jquery/src/core/access.js +65 -0
  134. data/public/node_modules/pivottable/node_modules/jquery/src/core/init.js +134 -0
  135. data/public/node_modules/pivottable/node_modules/jquery/src/core/parseHTML.js +41 -0
  136. data/public/node_modules/pivottable/node_modules/jquery/src/core/ready.js +103 -0
  137. data/public/node_modules/pivottable/node_modules/jquery/src/core/support.js +18 -0
  138. data/public/node_modules/pivottable/node_modules/jquery/src/core/var/rsingleTag.js +5 -0
  139. data/public/node_modules/pivottable/node_modules/jquery/src/css.js +515 -0
  140. data/public/node_modules/pivottable/node_modules/jquery/src/css/addGetHookIf.js +24 -0
  141. data/public/node_modules/pivottable/node_modules/jquery/src/css/adjustCSS.js +65 -0
  142. data/public/node_modules/pivottable/node_modules/jquery/src/css/curCSS.js +60 -0
  143. data/public/node_modules/pivottable/node_modules/jquery/src/css/defaultDisplay.js +72 -0
  144. data/public/node_modules/pivottable/node_modules/jquery/src/css/hiddenVisibleSelectors.js +18 -0
  145. data/public/node_modules/pivottable/node_modules/jquery/src/css/showHide.js +48 -0
  146. data/public/node_modules/pivottable/node_modules/jquery/src/css/support.js +121 -0
  147. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/cssExpand.js +3 -0
  148. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/getStyles.js +15 -0
  149. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/isHidden.js +16 -0
  150. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/rmargin.js +3 -0
  151. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/rnumnonpx.js +5 -0
  152. data/public/node_modules/pivottable/node_modules/jquery/src/css/var/swap.js +24 -0
  153. data/public/node_modules/pivottable/node_modules/jquery/src/data.js +187 -0
  154. data/public/node_modules/pivottable/node_modules/jquery/src/data/Data.js +200 -0
  155. data/public/node_modules/pivottable/node_modules/jquery/src/data/accepts.js +20 -0
  156. data/public/node_modules/pivottable/node_modules/jquery/src/data/support.js +23 -0
  157. data/public/node_modules/pivottable/node_modules/jquery/src/data/var/acceptData.js +18 -0
  158. data/public/node_modules/pivottable/node_modules/jquery/src/data/var/dataPriv.js +5 -0
  159. data/public/node_modules/pivottable/node_modules/jquery/src/data/var/dataUser.js +5 -0
  160. data/public/node_modules/pivottable/node_modules/jquery/src/deferred.js +158 -0
  161. data/public/node_modules/pivottable/node_modules/jquery/src/deferred/exceptionHook.js +19 -0
  162. data/public/node_modules/pivottable/node_modules/jquery/src/deprecated.js +32 -0
  163. data/public/node_modules/pivottable/node_modules/jquery/src/dimensions.js +54 -0
  164. data/public/node_modules/pivottable/node_modules/jquery/src/effects.js +629 -0
  165. data/public/node_modules/pivottable/node_modules/jquery/src/effects/Tween.js +121 -0
  166. data/public/node_modules/pivottable/node_modules/jquery/src/effects/animatedSelector.js +13 -0
  167. data/public/node_modules/pivottable/node_modules/jquery/src/effects/support.js +58 -0
  168. data/public/node_modules/pivottable/node_modules/jquery/src/event.js +710 -0
  169. data/public/node_modules/pivottable/node_modules/jquery/src/event/ajax.js +20 -0
  170. data/public/node_modules/pivottable/node_modules/jquery/src/event/alias.js +27 -0
  171. data/public/node_modules/pivottable/node_modules/jquery/src/event/focusin.js +53 -0
  172. data/public/node_modules/pivottable/node_modules/jquery/src/event/support.js +9 -0
  173. data/public/node_modules/pivottable/node_modules/jquery/src/event/trigger.js +199 -0
  174. data/public/node_modules/pivottable/node_modules/jquery/src/exports/amd.js +24 -0
  175. data/public/node_modules/pivottable/node_modules/jquery/src/exports/global.js +26 -0
  176. data/public/node_modules/pivottable/node_modules/jquery/src/intro.js +44 -0
  177. data/public/node_modules/pivottable/node_modules/jquery/src/jquery.js +37 -0
  178. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation.js +481 -0
  179. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/_evalUrl.js +20 -0
  180. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/buildFragment.js +102 -0
  181. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/createSafeFragment.js +20 -0
  182. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/getAll.js +21 -0
  183. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/setGlobalEval.js +20 -0
  184. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/support.js +33 -0
  185. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/var/nodeNames.js +5 -0
  186. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/var/rcheckableType.js +3 -0
  187. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/var/rleadingWhitespace.js +3 -0
  188. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/var/rscriptType.js +3 -0
  189. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/var/rtagName.js +3 -0
  190. data/public/node_modules/pivottable/node_modules/jquery/src/manipulation/wrapMap.js +27 -0
  191. data/public/node_modules/pivottable/node_modules/jquery/src/offset.js +218 -0
  192. data/public/node_modules/pivottable/node_modules/jquery/src/outro.js +2 -0
  193. data/public/node_modules/pivottable/node_modules/jquery/src/queue.js +143 -0
  194. data/public/node_modules/pivottable/node_modules/jquery/src/queue/delay.js +22 -0
  195. data/public/node_modules/pivottable/node_modules/jquery/src/selector-native.js +211 -0
  196. data/public/node_modules/pivottable/node_modules/jquery/src/selector-sizzle.js +14 -0
  197. data/public/node_modules/pivottable/node_modules/jquery/src/selector.js +1 -0
  198. data/public/node_modules/pivottable/node_modules/jquery/src/serialize.js +125 -0
  199. data/public/node_modules/pivottable/node_modules/jquery/src/support.js +63 -0
  200. data/public/node_modules/pivottable/node_modules/jquery/src/traversing.js +175 -0
  201. data/public/node_modules/pivottable/node_modules/jquery/src/traversing/findFilter.js +100 -0
  202. data/public/node_modules/pivottable/node_modules/jquery/src/traversing/var/dir.js +20 -0
  203. data/public/node_modules/pivottable/node_modules/jquery/src/traversing/var/rneedsContext.js +6 -0
  204. data/public/node_modules/pivottable/node_modules/jquery/src/traversing/var/siblings.js +15 -0
  205. data/public/node_modules/pivottable/node_modules/jquery/src/var/arr.js +3 -0
  206. data/public/node_modules/pivottable/node_modules/jquery/src/var/class2type.js +5 -0
  207. data/public/node_modules/pivottable/node_modules/jquery/src/var/concat.js +5 -0
  208. data/public/node_modules/pivottable/node_modules/jquery/src/var/deletedIds.js +3 -0
  209. data/public/node_modules/pivottable/node_modules/jquery/src/var/document.js +3 -0
  210. data/public/node_modules/pivottable/node_modules/jquery/src/var/documentElement.js +5 -0
  211. data/public/node_modules/pivottable/node_modules/jquery/src/var/hasOwn.js +5 -0
  212. data/public/node_modules/pivottable/node_modules/jquery/src/var/indexOf.js +5 -0
  213. data/public/node_modules/pivottable/node_modules/jquery/src/var/pnum.js +3 -0
  214. data/public/node_modules/pivottable/node_modules/jquery/src/var/push.js +5 -0
  215. data/public/node_modules/pivottable/node_modules/jquery/src/var/rcssNum.js +7 -0
  216. data/public/node_modules/pivottable/node_modules/jquery/src/var/rnotwhite.js +3 -0
  217. data/public/node_modules/pivottable/node_modules/jquery/src/var/slice.js +5 -0
  218. data/public/node_modules/pivottable/node_modules/jquery/src/var/support.js +5 -0
  219. data/public/node_modules/pivottable/node_modules/jquery/src/var/toString.js +5 -0
  220. data/public/node_modules/pivottable/node_modules/jquery/src/wrap.js +79 -0
  221. data/public/node_modules/pivottable/package.json +70 -0
  222. data/public/node_modules/pivottable/pivot.coffee +957 -0
  223. data/public/node_modules/pivottable/pivot.es.coffee +57 -0
  224. data/public/node_modules/pivottable/pivot.fr.coffee +57 -0
  225. data/public/node_modules/pivottable/pivot.nl.coffee +55 -0
  226. data/public/node_modules/pivottable/pivot.pt.coffee +80 -0
  227. data/public/node_modules/pivottable/pivot.ru.coffee +56 -0
  228. data/public/node_modules/pivottable/pivot.tr.coffee +80 -0
  229. data/public/node_modules/pivottable/pivottable.jquery.json +33 -0
  230. data/public/node_modules/pivottable/tests/boot.js +152 -0
  231. data/public/node_modules/pivottable/tests/index.html +35 -0
  232. data/public/node_modules/pivottable/tests/pivot_spec.coffee +458 -0
  233. data/public/vendor.js +5 -0
  234. metadata +232 -1
@@ -0,0 +1,7 @@
1
+ define( [
2
+ "../var/pnum"
3
+ ], function( pnum ) {
4
+
5
+ return new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
6
+
7
+ } );
@@ -0,0 +1,3 @@
1
+ define( function() {
2
+ return ( /\S+/g );
3
+ } );
@@ -0,0 +1,5 @@
1
+ define( [
2
+ "./arr"
3
+ ], function( arr ) {
4
+ return arr.slice;
5
+ } );
@@ -0,0 +1,5 @@
1
+ define( function() {
2
+
3
+ // All support tests are defined in their respective modules.
4
+ return {};
5
+ } );
@@ -0,0 +1,5 @@
1
+ define( [
2
+ "./class2type"
3
+ ], function( class2type ) {
4
+ return class2type.toString;
5
+ } );
@@ -0,0 +1,79 @@
1
+ define( [
2
+ "./core",
3
+ "./core/init",
4
+ "./manipulation", // clone
5
+ "./traversing" // parent, contents
6
+ ], function( jQuery ) {
7
+
8
+ jQuery.fn.extend( {
9
+ wrapAll: function( html ) {
10
+ var wrap;
11
+
12
+ if ( jQuery.isFunction( html ) ) {
13
+ return this.each( function( i ) {
14
+ jQuery( this ).wrapAll( html.call( this, i ) );
15
+ } );
16
+ }
17
+
18
+ if ( this[ 0 ] ) {
19
+
20
+ // The elements to wrap the target around
21
+ wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
22
+
23
+ if ( this[ 0 ].parentNode ) {
24
+ wrap.insertBefore( this[ 0 ] );
25
+ }
26
+
27
+ wrap.map( function() {
28
+ var elem = this;
29
+
30
+ while ( elem.firstElementChild ) {
31
+ elem = elem.firstElementChild;
32
+ }
33
+
34
+ return elem;
35
+ } ).append( this );
36
+ }
37
+
38
+ return this;
39
+ },
40
+
41
+ wrapInner: function( html ) {
42
+ if ( jQuery.isFunction( html ) ) {
43
+ return this.each( function( i ) {
44
+ jQuery( this ).wrapInner( html.call( this, i ) );
45
+ } );
46
+ }
47
+
48
+ return this.each( function() {
49
+ var self = jQuery( this ),
50
+ contents = self.contents();
51
+
52
+ if ( contents.length ) {
53
+ contents.wrapAll( html );
54
+
55
+ } else {
56
+ self.append( html );
57
+ }
58
+ } );
59
+ },
60
+
61
+ wrap: function( html ) {
62
+ var isFunction = jQuery.isFunction( html );
63
+
64
+ return this.each( function( i ) {
65
+ jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
66
+ } );
67
+ },
68
+
69
+ unwrap: function() {
70
+ return this.parent().each( function() {
71
+ if ( !jQuery.nodeName( this, "body" ) ) {
72
+ jQuery( this ).replaceWith( this.childNodes );
73
+ }
74
+ } ).end();
75
+ }
76
+ } );
77
+
78
+ return jQuery;
79
+ } );
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "pivottable",
3
+ "version": "2.0.2",
4
+ "description": "Javascript Pivot Table (aka Pivot Grid, Pivot Chart, Cross-Tab) implementation with drag'n'drop",
5
+ "main": "dist/pivot.js",
6
+ "keywords": [
7
+ "pivot",
8
+ "crosstab",
9
+ "grid",
10
+ "table",
11
+ "pivottable",
12
+ "pivotgrid",
13
+ "pivotchart",
14
+ "jquery",
15
+ "jquery-plugin"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git://github.com/nicolaskruchten/pivottable.git"
20
+ },
21
+ "author": {
22
+ "name": "Nicolas Kruchten"
23
+ },
24
+ "license": "MIT",
25
+ "bugs": {
26
+ "url": "https://github.com/nicolaskruchten/pivottable/issues"
27
+ },
28
+ "homepage": "https://github.com/nicolaskruchten/pivottable",
29
+ "dependencies": {
30
+ "jquery": ">=1.9.0"
31
+ },
32
+ "devDependencies": {
33
+ "gulp-coffee": "^2.2.0",
34
+ "gulp-uglify": "^1.0.2",
35
+ "gulp-sourcemaps": "^1.2.8",
36
+ "gulp": "^3.8.10",
37
+ "run-sequence": "^1.0.2",
38
+ "gulp-tag-version": "^1.2.1",
39
+ "gulp-git": "^0.5.5",
40
+ "gulp-bump": "^0.1.11",
41
+ "gulp-filter": "^1.0.2",
42
+ "gulp-util": "^3.0.1",
43
+ "gulp-rename": "^1.2.0",
44
+ "gulp-minify-css": "^0.3.11",
45
+ "gulp-concat": "^2.4.2"
46
+ },
47
+ "gitHead": "a2f694fc3011471a7fe4d300c9f4b6c6edf1cffe",
48
+ "_id": "pivottable@2.0.2",
49
+ "scripts": {},
50
+ "_shasum": "901dea3a4a37fca6db4e1b658364bce803cf9380",
51
+ "_from": "pivottable@*",
52
+ "_npmVersion": "2.11.3",
53
+ "_nodeVersion": "0.12.7",
54
+ "_npmUser": {
55
+ "name": "nicolaskruchten",
56
+ "email": "nicolas@kruchten.com"
57
+ },
58
+ "dist": {
59
+ "shasum": "901dea3a4a37fca6db4e1b658364bce803cf9380",
60
+ "tarball": "https://registry.npmjs.org/pivottable/-/pivottable-2.0.2.tgz"
61
+ },
62
+ "maintainers": [
63
+ {
64
+ "name": "nicolaskruchten",
65
+ "email": "nicolas@kruchten.com"
66
+ }
67
+ ],
68
+ "directories": {},
69
+ "_resolved": "https://registry.npmjs.org/pivottable/-/pivottable-2.0.2.tgz"
70
+ }
@@ -0,0 +1,957 @@
1
+ callWithJQuery = (pivotModule) ->
2
+ if typeof exports is "object" and typeof module is "object" # CommonJS
3
+ pivotModule require("jquery")
4
+ else if typeof define is "function" and define.amd # AMD
5
+ define ["jquery"], pivotModule
6
+ # Plain browser env
7
+ else
8
+ pivotModule jQuery
9
+
10
+ callWithJQuery ($) ->
11
+
12
+ ###
13
+ Utilities
14
+ ###
15
+
16
+ addSeparators = (nStr, thousandsSep, decimalSep) ->
17
+ nStr += ''
18
+ x = nStr.split('.')
19
+ x1 = x[0]
20
+ x2 = if x.length > 1 then decimalSep + x[1] else ''
21
+ rgx = /(\d+)(\d{3})/
22
+ x1 = x1.replace(rgx, '$1' + thousandsSep + '$2') while rgx.test(x1)
23
+ return x1 + x2
24
+
25
+ numberFormat = (opts) ->
26
+ defaults =
27
+ digitsAfterDecimal: 2, scaler: 1,
28
+ thousandsSep: ",", decimalSep: "."
29
+ prefix: "", suffix: ""
30
+ showZero: false
31
+ opts = $.extend defaults, opts
32
+ (x) ->
33
+ return "" if isNaN(x) or not isFinite(x)
34
+ return "" if x == 0 and not opts.showZero
35
+ result = addSeparators (opts.scaler*x).toFixed(opts.digitsAfterDecimal), opts.thousandsSep, opts.decimalSep
36
+ return ""+opts.prefix+result+opts.suffix
37
+
38
+ #aggregator templates default to US number formatting but this is overrideable
39
+ usFmt = numberFormat()
40
+ usFmtInt = numberFormat(digitsAfterDecimal: 0)
41
+ usFmtPct = numberFormat(digitsAfterDecimal:1, scaler: 100, suffix: "%")
42
+
43
+ aggregatorTemplates =
44
+ count: (formatter=usFmtInt) -> () -> (data, rowKey, colKey) ->
45
+ count: 0
46
+ push: -> @count++
47
+ value: -> @count
48
+ format: formatter
49
+
50
+ countUnique: (formatter=usFmtInt) -> ([attr]) -> (data, rowKey, colKey) ->
51
+ uniq: []
52
+ push: (record) -> @uniq.push(record[attr]) if record[attr] not in @uniq
53
+ value: -> @uniq.length
54
+ format: formatter
55
+ numInputs: if attr? then 0 else 1
56
+
57
+ listUnique: (sep) -> ([attr]) -> (data, rowKey, colKey) ->
58
+ uniq: []
59
+ push: (record) -> @uniq.push(record[attr]) if record[attr] not in @uniq
60
+ value: -> @uniq.join sep
61
+ format: (x) -> x
62
+ numInputs: if attr? then 0 else 1
63
+
64
+ sum: (formatter=usFmt) -> ([attr]) -> (data, rowKey, colKey) ->
65
+ sum: 0
66
+ push: (record) -> @sum += parseFloat(record[attr]) if not isNaN parseFloat(record[attr])
67
+ value: -> @sum
68
+ format: formatter
69
+ numInputs: if attr? then 0 else 1
70
+
71
+ min: (formatter=usFmt) -> ([attr]) -> (data, rowKey, colKey) ->
72
+ val: null
73
+ push: (record) ->
74
+ x = parseFloat(record[attr])
75
+ if not isNaN x then @val = Math.min(x, @val ? x)
76
+ value: -> @val
77
+ format: formatter
78
+ numInputs: if attr? then 0 else 1
79
+
80
+ max: (formatter=usFmt) -> ([attr]) -> (data, rowKey, colKey) ->
81
+ val: null
82
+ push: (record) ->
83
+ x = parseFloat(record[attr])
84
+ if not isNaN x then @val = Math.max(x, @val ? x)
85
+ value: -> @val
86
+ format: formatter
87
+ numInputs: if attr? then 0 else 1
88
+
89
+ average: (formatter=usFmt) -> ([attr]) -> (data, rowKey, colKey) ->
90
+ sum: 0
91
+ len: 0
92
+ push: (record) ->
93
+ if not isNaN parseFloat(record[attr])
94
+ @sum += parseFloat(record[attr])
95
+ @len++
96
+ value: -> @sum/@len
97
+ format: formatter
98
+ numInputs: if attr? then 0 else 1
99
+
100
+ sumOverSum: (formatter=usFmt) -> ([num, denom]) -> (data, rowKey, colKey) ->
101
+ sumNum: 0
102
+ sumDenom: 0
103
+ push: (record) ->
104
+ @sumNum += parseFloat(record[num]) if not isNaN parseFloat(record[num])
105
+ @sumDenom += parseFloat(record[denom]) if not isNaN parseFloat(record[denom])
106
+ value: -> @sumNum/@sumDenom
107
+ format: formatter
108
+ numInputs: if num? and denom? then 0 else 2
109
+
110
+ sumOverSumBound80: (upper=true, formatter=usFmt) -> ([num, denom]) -> (data, rowKey, colKey) ->
111
+ sumNum: 0
112
+ sumDenom: 0
113
+ push: (record) ->
114
+ @sumNum += parseFloat(record[num]) if not isNaN parseFloat(record[num])
115
+ @sumDenom += parseFloat(record[denom]) if not isNaN parseFloat(record[denom])
116
+ value: ->
117
+ sign = if upper then 1 else -1
118
+ (0.821187207574908/@sumDenom + @sumNum/@sumDenom + 1.2815515655446004*sign*
119
+ Math.sqrt(0.410593603787454/ (@sumDenom*@sumDenom) + (@sumNum*(1 - @sumNum/ @sumDenom))/ (@sumDenom*@sumDenom)))/
120
+ (1 + 1.642374415149816/@sumDenom)
121
+ format: formatter
122
+ numInputs: if num? and denom? then 0 else 2
123
+
124
+ fractionOf: (wrapped, type="total", formatter=usFmtPct) -> (x...) -> (data, rowKey, colKey) ->
125
+ selector: {total:[[],[]],row:[rowKey,[]],col:[[],colKey]}[type]
126
+ inner: wrapped(x...)(data, rowKey, colKey)
127
+ push: (record) -> @inner.push record
128
+ format: formatter
129
+ value: -> @inner.value() / data.getAggregator(@selector...).inner.value()
130
+ numInputs: wrapped(x...)().numInputs
131
+
132
+ #default aggregators & renderers use US naming and number formatting
133
+ aggregators = do (tpl = aggregatorTemplates) ->
134
+ "Count": tpl.count(usFmtInt)
135
+ "Count Unique Values": tpl.countUnique(usFmtInt)
136
+ "List Unique Values": tpl.listUnique(", ")
137
+ "Sum": tpl.sum(usFmt)
138
+ "Integer Sum": tpl.sum(usFmtInt)
139
+ "Average": tpl.average(usFmt)
140
+ "Minimum": tpl.min(usFmt)
141
+ "Maximum": tpl.max(usFmt)
142
+ "Sum over Sum": tpl.sumOverSum(usFmt)
143
+ "80% Upper Bound": tpl.sumOverSumBound80(true, usFmt)
144
+ "80% Lower Bound": tpl.sumOverSumBound80(false, usFmt)
145
+ "Sum as Fraction of Total": tpl.fractionOf(tpl.sum(), "total", usFmtPct)
146
+ "Sum as Fraction of Rows": tpl.fractionOf(tpl.sum(), "row", usFmtPct)
147
+ "Sum as Fraction of Columns": tpl.fractionOf(tpl.sum(), "col", usFmtPct)
148
+ "Count as Fraction of Total": tpl.fractionOf(tpl.count(), "total", usFmtPct)
149
+ "Count as Fraction of Rows": tpl.fractionOf(tpl.count(), "row", usFmtPct)
150
+ "Count as Fraction of Columns": tpl.fractionOf(tpl.count(), "col", usFmtPct)
151
+
152
+ renderers =
153
+ "Table": (pvtData, opts) -> pivotTableRenderer(pvtData, opts)
154
+ "Table Barchart": (pvtData, opts) -> $(pivotTableRenderer(pvtData, opts)).barchart()
155
+ "Heatmap": (pvtData, opts) -> $(pivotTableRenderer(pvtData, opts)).heatmap()
156
+ "Row Heatmap": (pvtData, opts) -> $(pivotTableRenderer(pvtData, opts)).heatmap("rowheatmap")
157
+ "Col Heatmap": (pvtData, opts) -> $(pivotTableRenderer(pvtData, opts)).heatmap("colheatmap")
158
+
159
+ locales =
160
+ en:
161
+ aggregators: aggregators
162
+ renderers: renderers
163
+ localeStrings:
164
+ renderError: "An error occurred rendering the PivotTable results."
165
+ computeError: "An error occurred computing the PivotTable results."
166
+ uiRenderError: "An error occurred rendering the PivotTable UI."
167
+ selectAll: "Select All"
168
+ selectNone: "Select None"
169
+ tooMany: "(too many to list)"
170
+ filterResults: "Filter results"
171
+ totals: "Totals" #for table renderer
172
+ vs: "vs" #for gchart renderer
173
+ by: "by" #for gchart renderer
174
+
175
+ #dateFormat deriver l10n requires month and day names to be passed in directly
176
+ mthNamesEn = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
177
+ dayNamesEn = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
178
+ zeroPad = (number) -> ("0"+number).substr(-2,2)
179
+
180
+ derivers =
181
+ bin: (col, binWidth) -> (record) -> record[col] - record[col] % binWidth
182
+ dateFormat: (col, formatString, utcOutput=false, mthNames=mthNamesEn, dayNames=dayNamesEn) ->
183
+ utc = if utcOutput then "UTC" else ""
184
+ (record) -> #thanks http://stackoverflow.com/a/12213072/112871
185
+ date = new Date(Date.parse(record[col]))
186
+ if isNaN(date) then return ""
187
+ formatString.replace /%(.)/g, (m, p) ->
188
+ switch p
189
+ when "y" then date["get#{utc}FullYear"]()
190
+ when "m" then zeroPad(date["get#{utc}Month"]()+1)
191
+ when "n" then mthNames[date["get#{utc}Month"]()]
192
+ when "d" then zeroPad(date["get#{utc}Date"]())
193
+ when "w" then dayNames[date["get#{utc}Day"]()]
194
+ when "x" then date["get#{utc}Day"]()
195
+ when "H" then zeroPad(date["get#{utc}Hours"]())
196
+ when "M" then zeroPad(date["get#{utc}Minutes"]())
197
+ when "S" then zeroPad(date["get#{utc}Seconds"]())
198
+ else "%" + p
199
+
200
+ naturalSort = (as, bs) => #thanks http://stackoverflow.com/a/4373421/112871
201
+ rx = /(\d+)|(\D+)/g
202
+ rd = /\d/
203
+ rz = /^0/
204
+ if typeof as is "number" or typeof bs is "number"
205
+ return 1 if isNaN(as)
206
+ return -1 if isNaN(bs)
207
+ return as - bs
208
+ a = String(as).toLowerCase()
209
+ b = String(bs).toLowerCase()
210
+ return 0 if a is b
211
+ return (if a > b then 1 else -1) unless rd.test(a) and rd.test(b)
212
+ a = a.match(rx)
213
+ b = b.match(rx)
214
+ while a.length and b.length
215
+ a1 = a.shift()
216
+ b1 = b.shift()
217
+ if a1 isnt b1
218
+ if rd.test(a1) and rd.test(b1)
219
+ return a1.replace(rz, ".0") - b1.replace(rz, ".0")
220
+ else
221
+ return (if a1 > b1 then 1 else -1)
222
+ a.length - b.length
223
+
224
+ sortAs = (order) ->
225
+ mapping = {}
226
+ for i, x of order
227
+ mapping[x] = i
228
+ (a, b) ->
229
+ if mapping[a]? and mapping[b]?
230
+ return mapping[a] - mapping[b]
231
+ else if mapping[a]?
232
+ return -1
233
+ else if mapping[b]?
234
+ return 1
235
+ else
236
+ return naturalSort(a,b)
237
+
238
+ getSort = (sorters, attr) ->
239
+ sort = sorters(attr)
240
+ if $.isFunction(sort)
241
+ return sort
242
+ else
243
+ return naturalSort
244
+
245
+ ###
246
+ Data Model class
247
+ ###
248
+
249
+ class PivotData
250
+ constructor: (input, opts) ->
251
+ @aggregator = opts.aggregator
252
+ @aggregatorName = opts.aggregatorName
253
+ @colAttrs = opts.cols
254
+ @rowAttrs = opts.rows
255
+ @valAttrs = opts.vals
256
+ @sorters = opts.sorters
257
+ @tree = {}
258
+ @rowKeys = []
259
+ @colKeys = []
260
+ @rowTotals = {}
261
+ @colTotals = {}
262
+ @allTotal = @aggregator(this, [], [])
263
+ @sorted = false
264
+
265
+ # iterate through input, accumulating data for cells
266
+ PivotData.forEachRecord input, opts.derivedAttributes, (record) =>
267
+ @processRecord(record) if opts.filter(record)
268
+
269
+ #can handle arrays or jQuery selections of tables
270
+ @forEachRecord = (input, derivedAttributes, f) ->
271
+ if $.isEmptyObject derivedAttributes
272
+ addRecord = f
273
+ else
274
+ addRecord = (record) ->
275
+ record[k] = v(record) ? record[k] for k, v of derivedAttributes
276
+ f(record)
277
+
278
+ #if it's a function, have it call us back
279
+ if $.isFunction(input)
280
+ input(addRecord)
281
+ else if $.isArray(input)
282
+ if $.isArray(input[0]) #array of arrays
283
+ for own i, compactRecord of input when i > 0
284
+ record = {}
285
+ record[k] = compactRecord[j] for own j, k of input[0]
286
+ addRecord(record)
287
+ else #array of objects
288
+ addRecord(record) for record in input
289
+ else if input instanceof jQuery
290
+ tblCols = []
291
+ $("thead > tr > th", input).each (i) -> tblCols.push $(this).text()
292
+ $("tbody > tr", input).each (i) ->
293
+ record = {}
294
+ $("td", this).each (j) -> record[tblCols[j]] = $(this).text()
295
+ addRecord(record)
296
+ else
297
+ throw new Error("unknown input format")
298
+
299
+ #converts to [{attr:val, attr:val},{attr:val, attr:val}] using method above
300
+ @convertToArray = (input) ->
301
+ result = []
302
+ PivotData.forEachRecord input, {}, (record) -> result.push record
303
+ return result
304
+
305
+ arrSort: (attrs) =>
306
+ sortersArr = (getSort(@sorters, a) for a in attrs)
307
+ (a,b) ->
308
+ for own i, sorter of sortersArr
309
+ comparison = sorter(a[i], b[i])
310
+ return comparison if comparison != 0
311
+ return 0
312
+
313
+ sortKeys: () =>
314
+ if not @sorted
315
+ @sorted = true
316
+ @rowKeys.sort @arrSort(@rowAttrs)
317
+ @colKeys.sort @arrSort(@colAttrs)
318
+
319
+ getColKeys: () =>
320
+ @sortKeys()
321
+ return @colKeys
322
+
323
+ getRowKeys: () =>
324
+ @sortKeys()
325
+ return @rowKeys
326
+
327
+ processRecord: (record) -> #this code is called in a tight loop
328
+ colKey = []
329
+ rowKey = []
330
+ colKey.push record[x] ? "null" for x in @colAttrs
331
+ rowKey.push record[x] ? "null" for x in @rowAttrs
332
+ flatRowKey = rowKey.join(String.fromCharCode(0))
333
+ flatColKey = colKey.join(String.fromCharCode(0))
334
+
335
+ @allTotal.push record
336
+
337
+ if rowKey.length != 0
338
+ if not @rowTotals[flatRowKey]
339
+ @rowKeys.push rowKey
340
+ @rowTotals[flatRowKey] = @aggregator(this, rowKey, [])
341
+ @rowTotals[flatRowKey].push record
342
+
343
+ if colKey.length != 0
344
+ if not @colTotals[flatColKey]
345
+ @colKeys.push colKey
346
+ @colTotals[flatColKey] = @aggregator(this, [], colKey)
347
+ @colTotals[flatColKey].push record
348
+
349
+ if colKey.length != 0 and rowKey.length != 0
350
+ if not @tree[flatRowKey]
351
+ @tree[flatRowKey] = {}
352
+ if not @tree[flatRowKey][flatColKey]
353
+ @tree[flatRowKey][flatColKey] = @aggregator(this, rowKey, colKey)
354
+ @tree[flatRowKey][flatColKey].push record
355
+
356
+ getAggregator: (rowKey, colKey) =>
357
+ flatRowKey = rowKey.join(String.fromCharCode(0))
358
+ flatColKey = colKey.join(String.fromCharCode(0))
359
+ if rowKey.length == 0 and colKey.length == 0
360
+ agg = @allTotal
361
+ else if rowKey.length == 0
362
+ agg = @colTotals[flatColKey]
363
+ else if colKey.length == 0
364
+ agg = @rowTotals[flatRowKey]
365
+ else
366
+ agg = @tree[flatRowKey][flatColKey]
367
+ return agg ? {value: (-> null), format: -> ""}
368
+
369
+ #expose these to the outside world
370
+ $.pivotUtilities = {aggregatorTemplates, aggregators, renderers, derivers, locales,
371
+ naturalSort, numberFormat, sortAs, PivotData}
372
+
373
+ ###
374
+ Default Renderer for hierarchical table layout
375
+ ###
376
+
377
+ pivotTableRenderer = (pivotData, opts) ->
378
+
379
+ defaults =
380
+ localeStrings:
381
+ totals: "Totals"
382
+
383
+ opts = $.extend defaults, opts
384
+
385
+ colAttrs = pivotData.colAttrs
386
+ rowAttrs = pivotData.rowAttrs
387
+ rowKeys = pivotData.getRowKeys()
388
+ colKeys = pivotData.getColKeys()
389
+
390
+ #now actually build the output
391
+ result = document.createElement("table")
392
+ result.className = "pvtTable"
393
+
394
+ #helper function for setting row/col-span in pivotTableRenderer
395
+ spanSize = (arr, i, j) ->
396
+ if i != 0
397
+ noDraw = true
398
+ for x in [0..j]
399
+ if arr[i-1][x] != arr[i][x]
400
+ noDraw = false
401
+ if noDraw
402
+ return -1 #do not draw cell
403
+ len = 0
404
+ while i+len < arr.length
405
+ stop = false
406
+ for x in [0..j]
407
+ stop = true if arr[i][x] != arr[i+len][x]
408
+ break if stop
409
+ len++
410
+ return len
411
+
412
+ #the first few rows are for col headers
413
+ for own j, c of colAttrs
414
+ tr = document.createElement("tr")
415
+ if parseInt(j) == 0 and rowAttrs.length != 0
416
+ th = document.createElement("th")
417
+ th.setAttribute("colspan", rowAttrs.length)
418
+ th.setAttribute("rowspan", colAttrs.length)
419
+ tr.appendChild th
420
+ th = document.createElement("th")
421
+ th.className = "pvtAxisLabel"
422
+ th.textContent = c
423
+ tr.appendChild th
424
+ for own i, colKey of colKeys
425
+ x = spanSize(colKeys, parseInt(i), parseInt(j))
426
+ if x != -1
427
+ th = document.createElement("th")
428
+ th.className = "pvtColLabel"
429
+ th.textContent = colKey[j]
430
+ th.setAttribute("colspan", x)
431
+ if parseInt(j) == colAttrs.length-1 and rowAttrs.length != 0
432
+ th.setAttribute("rowspan", 2)
433
+ tr.appendChild th
434
+ if parseInt(j) == 0
435
+ th = document.createElement("th")
436
+ th.className = "pvtTotalLabel"
437
+ th.innerHTML = opts.localeStrings.totals
438
+ th.setAttribute("rowspan", colAttrs.length + (if rowAttrs.length ==0 then 0 else 1))
439
+ tr.appendChild th
440
+ result.appendChild tr
441
+
442
+ #then a row for row header headers
443
+ if rowAttrs.length !=0
444
+ tr = document.createElement("tr")
445
+ for own i, r of rowAttrs
446
+ th = document.createElement("th")
447
+ th.className = "pvtAxisLabel"
448
+ th.textContent = r
449
+ tr.appendChild th
450
+ th = document.createElement("th")
451
+ if colAttrs.length ==0
452
+ th.className = "pvtTotalLabel"
453
+ th.innerHTML = opts.localeStrings.totals
454
+ tr.appendChild th
455
+ result.appendChild tr
456
+
457
+ #now the actual data rows, with their row headers and totals
458
+ for own i, rowKey of rowKeys
459
+ tr = document.createElement("tr")
460
+ for own j, txt of rowKey
461
+ x = spanSize(rowKeys, parseInt(i), parseInt(j))
462
+ if x != -1
463
+ th = document.createElement("th")
464
+ th.className = "pvtRowLabel"
465
+ th.textContent = txt
466
+ th.setAttribute("rowspan", x)
467
+ if parseInt(j) == rowAttrs.length-1 and colAttrs.length !=0
468
+ th.setAttribute("colspan",2)
469
+ tr.appendChild th
470
+ for own j, colKey of colKeys #this is the tight loop
471
+ aggregator = pivotData.getAggregator(rowKey, colKey)
472
+ val = aggregator.value()
473
+ td = document.createElement("td")
474
+ td.className = "pvtVal row#{i} col#{j}"
475
+ td.textContent = aggregator.format(val)
476
+ td.setAttribute("data-value", val)
477
+ tr.appendChild td
478
+
479
+ totalAggregator = pivotData.getAggregator(rowKey, [])
480
+ val = totalAggregator.value()
481
+ td = document.createElement("td")
482
+ td.className = "pvtTotal rowTotal"
483
+ td.textContent = totalAggregator.format(val)
484
+ td.setAttribute("data-value", val)
485
+ td.setAttribute("data-for", "row"+i)
486
+ tr.appendChild td
487
+ result.appendChild tr
488
+
489
+ #finally, the row for col totals, and a grand total
490
+ tr = document.createElement("tr")
491
+ th = document.createElement("th")
492
+ th.className = "pvtTotalLabel"
493
+ th.innerHTML = opts.localeStrings.totals
494
+ th.setAttribute("colspan", rowAttrs.length + (if colAttrs.length == 0 then 0 else 1))
495
+ tr.appendChild th
496
+ for own j, colKey of colKeys
497
+ totalAggregator = pivotData.getAggregator([], colKey)
498
+ val = totalAggregator.value()
499
+ td = document.createElement("td")
500
+ td.className = "pvtTotal colTotal"
501
+ td.textContent = totalAggregator.format(val)
502
+ td.setAttribute("data-value", val)
503
+ td.setAttribute("data-for", "col"+j)
504
+ tr.appendChild td
505
+ totalAggregator = pivotData.getAggregator([], [])
506
+ val = totalAggregator.value()
507
+ td = document.createElement("td")
508
+ td.className = "pvtGrandTotal"
509
+ td.textContent = totalAggregator.format(val)
510
+ td.setAttribute("data-value", val)
511
+ tr.appendChild td
512
+ result.appendChild tr
513
+
514
+ #squirrel this away for later
515
+ result.setAttribute("data-numrows", rowKeys.length)
516
+ result.setAttribute("data-numcols", colKeys.length)
517
+
518
+ return result
519
+
520
+ ###
521
+ Pivot Table core: create PivotData object and call Renderer on it
522
+ ###
523
+
524
+ $.fn.pivot = (input, opts) ->
525
+ defaults =
526
+ cols : []
527
+ rows: []
528
+ vals: []
529
+ filter: -> true
530
+ aggregator: aggregatorTemplates.count()()
531
+ aggregatorName: "Count"
532
+ sorters: ->
533
+ derivedAttributes: {},
534
+ renderer: pivotTableRenderer
535
+ rendererOptions: null
536
+ localeStrings: locales.en.localeStrings
537
+
538
+ opts = $.extend defaults, opts
539
+
540
+ result = null
541
+ try
542
+ pivotData = new PivotData(input, opts)
543
+ try
544
+ result = opts.renderer(pivotData, opts.rendererOptions)
545
+ catch e
546
+ console.error(e.stack) if console?
547
+ result = $("<span>").html opts.localeStrings.renderError
548
+ catch e
549
+ console.error(e.stack) if console?
550
+ result = $("<span>").html opts.localeStrings.computeError
551
+
552
+ x = this[0]
553
+ x.removeChild(x.lastChild) while x.hasChildNodes()
554
+ return @append result
555
+
556
+
557
+ ###
558
+ Pivot Table UI: calls Pivot Table core above with options set by user
559
+ ###
560
+
561
+ $.fn.pivotUI = (input, inputOpts, overwrite = false, locale="en") ->
562
+ if not locales[locale]?
563
+ locale = "en"
564
+ defaults =
565
+ derivedAttributes: {}
566
+ aggregators: locales[locale].aggregators
567
+ renderers: locales[locale].renderers
568
+ hiddenAttributes: []
569
+ menuLimit: 200
570
+ cols: [], rows: [], vals: []
571
+ exclusions: {}
572
+ inclusions: {}
573
+ unusedAttrsVertical: 85
574
+ autoSortUnusedAttrs: false
575
+ rendererOptions: localeStrings: locales[locale].localeStrings
576
+ onRefresh: null
577
+ filter: -> true
578
+ sorters: ->
579
+ localeStrings: locales[locale].localeStrings
580
+
581
+ existingOpts = @data "pivotUIOptions"
582
+ if not existingOpts? or overwrite
583
+ opts = $.extend defaults, inputOpts
584
+ else
585
+ opts = existingOpts
586
+
587
+ try
588
+ #cache the input in some useful form
589
+ input = PivotData.convertToArray(input)
590
+ tblCols = (k for own k of input[0])
591
+ tblCols.push c for own c of opts.derivedAttributes when (c not in tblCols)
592
+
593
+ #figure out the cardinality and some stats
594
+ axisValues = {}
595
+ axisValues[x] = {} for x in tblCols
596
+
597
+ PivotData.forEachRecord input, opts.derivedAttributes, (record) ->
598
+ for own k, v of record when opts.filter(record)
599
+ v ?= "null"
600
+ axisValues[k][v] ?= 0
601
+ axisValues[k][v]++
602
+
603
+ #start building the output
604
+ uiTable = $("<table>", "class": "pvtUi").attr("cellpadding", 5)
605
+
606
+ #renderer control
607
+ rendererControl = $("<td>")
608
+
609
+ renderer = $("<select>")
610
+ .addClass('pvtRenderer')
611
+ .appendTo(rendererControl)
612
+ .bind "change", -> refresh() #capture reference
613
+ for own x of opts.renderers
614
+ $("<option>").val(x).html(x).appendTo(renderer)
615
+
616
+
617
+ #axis list, including the double-click menu
618
+ colList = $("<td>").addClass('pvtAxisContainer pvtUnused')
619
+ shownAttributes = (c for c in tblCols when c not in opts.hiddenAttributes)
620
+
621
+ unusedAttrsVerticalAutoOverride = false
622
+ if opts.unusedAttrsVertical == "auto"
623
+ unusedAttrsVerticalAutoCutoff = 120 # legacy support
624
+ else
625
+ unusedAttrsVerticalAutoCutoff = parseInt opts.unusedAttrsVertical
626
+
627
+ if not isNaN(unusedAttrsVerticalAutoCutoff)
628
+ attrLength = 0
629
+ attrLength += a.length for a in shownAttributes
630
+ unusedAttrsVerticalAutoOverride = attrLength > unusedAttrsVerticalAutoCutoff
631
+
632
+ if opts.unusedAttrsVertical == true or unusedAttrsVerticalAutoOverride
633
+ colList.addClass('pvtVertList')
634
+ else
635
+ colList.addClass('pvtHorizList')
636
+
637
+ for own i, c of shownAttributes
638
+ do (c) ->
639
+ keys = (k for k of axisValues[c])
640
+ hasExcludedItem = false
641
+ valueList = $("<div>").addClass('pvtFilterBox').hide()
642
+
643
+ valueList.append $("<h4>").text("#{c} (#{keys.length})")
644
+ if keys.length > opts.menuLimit
645
+ valueList.append $("<p>").html(opts.localeStrings.tooMany)
646
+ else
647
+ btns = $("<p>").appendTo(valueList)
648
+ btns.append $("<button>", {type:"button"}).html(opts.localeStrings.selectAll).bind "click", ->
649
+ valueList.find("input:visible").prop "checked", true
650
+ btns.append $("<button>", {type:"button"}).html(opts.localeStrings.selectNone).bind "click", ->
651
+ valueList.find("input:visible").prop "checked", false
652
+ btns.append $("<br>")
653
+ btns.append $("<input>", {type: "text", placeholder: opts.localeStrings.filterResults, class: "pvtSearch"}).bind "keyup", ->
654
+ filter = $(this).val().toLowerCase()
655
+ valueList.find('.pvtCheckContainer p').each ->
656
+ testString = $(this).text().toLowerCase().indexOf(filter)
657
+ if testString isnt -1
658
+ $(this).show()
659
+ else
660
+ $(this).hide()
661
+
662
+ checkContainer = $("<div>").addClass("pvtCheckContainer").appendTo(valueList)
663
+
664
+ for k in keys.sort(getSort(opts.sorters, c))
665
+ v = axisValues[c][k]
666
+ filterItem = $("<label>")
667
+ filterItemExcluded = false
668
+ if opts.inclusions[c]
669
+ filterItemExcluded = (k not in opts.inclusions[c])
670
+ else if opts.exclusions[c]
671
+ filterItemExcluded = (k in opts.exclusions[c])
672
+ hasExcludedItem ||= filterItemExcluded
673
+ $("<input>")
674
+ .attr("type", "checkbox").addClass('pvtFilter')
675
+ .attr("checked", !filterItemExcluded).data("filter", [c,k])
676
+ .appendTo filterItem
677
+ filterItem.append $("<span>").text k
678
+ filterItem.append $("<span>").text " ("+v+")"
679
+ checkContainer.append $("<p>").append(filterItem)
680
+
681
+ updateFilter = ->
682
+ unselectedCount = valueList.find("[type='checkbox']").length -
683
+ valueList.find("[type='checkbox']:checked").length
684
+ if unselectedCount > 0
685
+ attrElem.addClass "pvtFilteredAttribute"
686
+ else
687
+ attrElem.removeClass "pvtFilteredAttribute"
688
+ if keys.length > opts.menuLimit
689
+ valueList.toggle()
690
+ else
691
+ valueList.toggle(0, refresh)
692
+
693
+ $("<p>").appendTo(valueList)
694
+ .append $("<button>", {type:"button"}).text("OK").bind "click", updateFilter
695
+
696
+ showFilterList = (e) ->
697
+ {left: clickLeft, top: clickTop, } = $(e.currentTarget).position()
698
+ valueList.css(left: clickLeft+10, top: clickTop+10).toggle()
699
+ valueList.find('.pvtSearch').val('')
700
+ valueList.find('.pvtCheckContainer p').show()
701
+
702
+ triangleLink = $("<span>").addClass('pvtTriangle').html(" &#x25BE;")
703
+ .bind "click", showFilterList
704
+
705
+ attrElem = $("<li>").addClass("axis_#{i}")
706
+ .append $("<span>").addClass('pvtAttr').text(c).data("attrName", c).append(triangleLink)
707
+ attrElem.addClass('pvtFilteredAttribute') if hasExcludedItem
708
+ colList.append(attrElem).append(valueList)
709
+
710
+ attrElem.bind "dblclick", showFilterList
711
+
712
+ tr1 = $("<tr>").appendTo(uiTable)
713
+
714
+ #aggregator menu and value area
715
+
716
+ aggregator = $("<select>").addClass('pvtAggregator')
717
+ .bind "change", -> refresh() #capture reference
718
+ for own x of opts.aggregators
719
+ aggregator.append $("<option>").val(x).html(x)
720
+
721
+ $("<td>").addClass('pvtVals')
722
+ .appendTo(tr1)
723
+ .append(aggregator)
724
+ .append($("<br>"))
725
+
726
+ #column axes
727
+ $("<td>").addClass('pvtAxisContainer pvtHorizList pvtCols').appendTo(tr1)
728
+
729
+ tr2 = $("<tr>").appendTo(uiTable)
730
+
731
+ #row axes
732
+ tr2.append $("<td>").addClass('pvtAxisContainer pvtRows').attr("valign", "top")
733
+
734
+ #the actual pivot table container
735
+ pivotTable = $("<td>")
736
+ .attr("valign", "top")
737
+ .addClass('pvtRendererArea')
738
+ .appendTo(tr2)
739
+
740
+ #finally the renderer dropdown and unused attribs are inserted at the requested location
741
+ if opts.unusedAttrsVertical == true or unusedAttrsVerticalAutoOverride
742
+ uiTable.find('tr:nth-child(1)').prepend rendererControl
743
+ uiTable.find('tr:nth-child(2)').prepend colList
744
+ else
745
+ uiTable.prepend $("<tr>").append(rendererControl).append(colList)
746
+
747
+ #render the UI in its default state
748
+ @html uiTable
749
+
750
+ #set up the UI initial state as requested by moving elements around
751
+
752
+ for x in opts.cols
753
+ @find(".pvtCols").append @find(".axis_#{$.inArray(x, shownAttributes)}")
754
+ for x in opts.rows
755
+ @find(".pvtRows").append @find(".axis_#{$.inArray(x, shownAttributes)}")
756
+ if opts.aggregatorName?
757
+ @find(".pvtAggregator").val opts.aggregatorName
758
+ if opts.rendererName?
759
+ @find(".pvtRenderer").val opts.rendererName
760
+
761
+ initialRender = true
762
+
763
+ #set up for refreshing
764
+ refreshDelayed = =>
765
+ subopts =
766
+ derivedAttributes: opts.derivedAttributes
767
+ localeStrings: opts.localeStrings
768
+ rendererOptions: opts.rendererOptions
769
+ sorters: opts.sorters
770
+ cols: [], rows: []
771
+
772
+ numInputsToProcess = opts.aggregators[aggregator.val()]([])().numInputs ? 0
773
+ vals = []
774
+ @find(".pvtRows li span.pvtAttr").each -> subopts.rows.push $(this).data("attrName")
775
+ @find(".pvtCols li span.pvtAttr").each -> subopts.cols.push $(this).data("attrName")
776
+ @find(".pvtVals select.pvtAttrDropdown").each ->
777
+ if numInputsToProcess == 0
778
+ $(this).remove()
779
+ else
780
+ numInputsToProcess--
781
+ vals.push $(this).val() if $(this).val() != ""
782
+
783
+ if numInputsToProcess != 0
784
+ pvtVals = @find(".pvtVals")
785
+ for x in [0...numInputsToProcess]
786
+ newDropdown = $("<select>")
787
+ .addClass('pvtAttrDropdown')
788
+ .append($("<option>"))
789
+ .bind "change", -> refresh()
790
+ for attr in shownAttributes
791
+ newDropdown.append($("<option>").val(attr).text(attr))
792
+ pvtVals.append(newDropdown)
793
+
794
+ if initialRender
795
+ vals = opts.vals
796
+ i = 0
797
+ @find(".pvtVals select.pvtAttrDropdown").each ->
798
+ $(this).val vals[i]
799
+ i++
800
+ initialRender = false
801
+
802
+ subopts.aggregatorName = aggregator.val()
803
+ subopts.vals = vals
804
+ subopts.aggregator = opts.aggregators[aggregator.val()](vals)
805
+ subopts.renderer = opts.renderers[renderer.val()]
806
+
807
+ #construct filter here
808
+ exclusions = {}
809
+ @find('input.pvtFilter').not(':checked').each ->
810
+ filter = $(this).data("filter")
811
+ if exclusions[filter[0]]?
812
+ exclusions[filter[0]].push( filter[1] )
813
+ else
814
+ exclusions[filter[0]] = [ filter[1] ]
815
+ #include inclusions when exclusions present
816
+ inclusions = {}
817
+ @find('input.pvtFilter:checked').each ->
818
+ filter = $(this).data("filter")
819
+ if exclusions[filter[0]]?
820
+ if inclusions[filter[0]]?
821
+ inclusions[filter[0]].push( filter[1] )
822
+ else
823
+ inclusions[filter[0]] = [ filter[1] ]
824
+
825
+ subopts.filter = (record) ->
826
+ return false if not opts.filter(record)
827
+ for k,excludedItems of exclusions
828
+ return false if ""+record[k] in excludedItems
829
+ return true
830
+
831
+ pivotTable.pivot(input,subopts)
832
+ pivotUIOptions = $.extend opts,
833
+ cols: subopts.cols
834
+ rows: subopts.rows
835
+ vals: vals
836
+ exclusions: exclusions
837
+ inclusions: inclusions
838
+ inclusionsInfo: inclusions #duplicated for backwards-compatibility
839
+ aggregatorName: aggregator.val()
840
+ rendererName: renderer.val()
841
+
842
+ @data "pivotUIOptions", pivotUIOptions
843
+
844
+ # if requested make sure unused columns are in alphabetical order
845
+ if opts.autoSortUnusedAttrs
846
+ unusedAttrsContainer = @find("td.pvtUnused.pvtAxisContainer")
847
+ $(unusedAttrsContainer).children("li")
848
+ .sort((a, b) => naturalSort($(a).text(), $(b).text()))
849
+ .appendTo unusedAttrsContainer
850
+
851
+ pivotTable.css("opacity", 1)
852
+ opts.onRefresh(pivotUIOptions) if opts.onRefresh?
853
+
854
+ refresh = =>
855
+ pivotTable.css("opacity", 0.5)
856
+ setTimeout refreshDelayed, 10
857
+
858
+ #the very first refresh will actually display the table
859
+ refresh()
860
+
861
+ @find(".pvtAxisContainer").sortable
862
+ update: (e, ui) -> refresh() if not ui.sender?
863
+ connectWith: @find(".pvtAxisContainer")
864
+ items: 'li'
865
+ placeholder: 'pvtPlaceholder'
866
+ catch e
867
+ console.error(e.stack) if console?
868
+ @html opts.localeStrings.uiRenderError
869
+ return this
870
+
871
+ ###
872
+ Heatmap post-processing
873
+ ###
874
+
875
+ $.fn.heatmap = (scope = "heatmap") ->
876
+ numRows = @data "numrows"
877
+ numCols = @data "numcols"
878
+
879
+ colorGen = (color, min, max) ->
880
+ hexGen = switch color
881
+ when "red" then (hex) -> "ff#{hex}#{hex}"
882
+ when "green" then (hex) -> "#{hex}ff#{hex}"
883
+ when "blue" then (hex) -> "#{hex}#{hex}ff"
884
+
885
+ return (x) ->
886
+ intensity = 255 - Math.round 255*(x-min)/(max-min)
887
+ hex = intensity.toString(16).split(".")[0]
888
+ hex = 0+hex if hex.length == 1
889
+ return hexGen(hex)
890
+
891
+ heatmapper = (scope, color) =>
892
+ forEachCell = (f) =>
893
+ @find(scope).each ->
894
+ x = $(this).data("value")
895
+ f(x, $(this)) if x? and isFinite(x)
896
+
897
+ values = []
898
+ forEachCell (x) -> values.push x
899
+ colorFor = colorGen color, Math.min(values...), Math.max(values...)
900
+ forEachCell (x, elem) -> elem.css "background-color", "#" + colorFor(x)
901
+
902
+ switch scope
903
+ when "heatmap"
904
+ heatmapper ".pvtVal", "red"
905
+ when "rowheatmap"
906
+ heatmapper ".pvtVal.row#{i}", "red" for i in [0...numRows]
907
+ when "colheatmap"
908
+ heatmapper ".pvtVal.col#{j}", "red" for j in [0...numCols]
909
+
910
+ heatmapper ".pvtTotal.rowTotal", "red"
911
+ heatmapper ".pvtTotal.colTotal", "red"
912
+
913
+ return this
914
+
915
+ ###
916
+ Barchart post-processing
917
+ ###
918
+
919
+ $.fn.barchart = ->
920
+ numRows = @data "numrows"
921
+ numCols = @data "numcols"
922
+
923
+ barcharter = (scope) =>
924
+ forEachCell = (f) =>
925
+ @find(scope).each ->
926
+ x = $(this).data("value")
927
+ f(x, $(this)) if x? and isFinite(x)
928
+
929
+ values = []
930
+ forEachCell (x) -> values.push x
931
+ max = Math.max(values...)
932
+ scaler = (x) -> 100*x/(1.4*max)
933
+ forEachCell (x, elem) ->
934
+ text = elem.text()
935
+ wrapper = $("<div>").css
936
+ "position": "relative"
937
+ "height": "55px"
938
+ wrapper.append $("<div>").css
939
+ "position": "absolute"
940
+ "bottom": 0
941
+ "left": 0
942
+ "right": 0
943
+ "height": scaler(x) + "%"
944
+ "background-color": "gray"
945
+ wrapper.append $("<div>").text(text).css
946
+ "position":"relative"
947
+ "padding-left":"5px"
948
+ "padding-right":"5px"
949
+
950
+ elem.css("padding": 0,"padding-top": "5px", "text-align": "center").html wrapper
951
+
952
+ barcharter ".pvtVal.row#{i}" for i in [0...numRows]
953
+ barcharter ".pvtTotal.colTotal"
954
+
955
+ return this
956
+
957
+