diff --git a/Gulpfile.js b/Gulpfile.js
index 7f22c38d913620aebd56f555b394dfbbac19928b..26fe35a6e304e708dd42e534cb5763688c2b9670 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -9,7 +9,8 @@ var basePath = 'resources/frontend_client/app/';
 
 var SRC = {
     css: [basePath + 'css/**/*.css', basePath + 'components/**/*.css'],
-    jsx: [basePath + 'query_builder/*.js']
+    jsx: [basePath + 'query_builder/*.js'],
+    appJS: [basePath + '**/*.js', '!' + basePath + 'bower_components/**/*.js', '!' + basePath + 'dist/*.js', '!' + basePath + 'query_builder/*.js', '!' + basePath + '/test/**/*.js']
 };
 
 var DEST = {
@@ -41,16 +42,21 @@ gulp.task('jsx', function () {
     return gulp.src(SRC.jsx)
         .pipe(concat('query_builder.js'))
         .pipe(react())
-        .pipe(gulp.dest(DEST.js))
-})
+        .pipe(gulp.dest(DEST.js));
+});
 
+gulp.task('build-js', function () {
+    return gulp.src(SRC.appJS)
+        .pipe(concat('app.js'))
+        .pipe(gulp.dest(DEST.js));
+});
 
 gulp.task('watch', function(){
     gulp.watch(SRC.css, ['css']);
     gulp.watch(SRC.jsx, ['jsx']);
+    gulp.watch(SRC.appJS, ['build-js']);
 });
 
+gulp.task('build', ['css', 'jsx', 'build-js']);
 
-gulp.task('build', ['css', 'jsx']);
-
-gulp.task('default', ['build','watch', 'jsx']);
+gulp.task('default', ['build', 'watch']);
diff --git a/resources/frontend_client/app/card/card.controllers.js b/resources/frontend_client/app/card/card.controllers.js
index f17ef83e23b4ed8bc8a9e95727c2460bc746eac5..15f557a98cc307c5dda2a926f757765bc9a8a77d 100644
--- a/resources/frontend_client/app/card/card.controllers.js
+++ b/resources/frontend_client/app/card/card.controllers.js
@@ -1462,6 +1462,9 @@ CardControllers.controller('CardDetailNew', [
 
                         }
                     },
+                    getDownloadLink: function () {
+                        return '/api/meta/dataset/csv/?query=' + encodeURIComponent(JSON.stringify($scope.model.card.dataset_query));
+                    },
                     cleanFilters: function (dataset_query) {
                         var filters = dataset_query.query.filter,
                             cleanFilters = [];
diff --git a/resources/frontend_client/app/query_builder/query_builder.js b/resources/frontend_client/app/query_builder/query_builder.js
index 90d34800f5414106a9c27a30f108bf66fb769f85..e246705ba1e126fffbfd3a4254497aec8b30d587 100644
--- a/resources/frontend_client/app/query_builder/query_builder.js
+++ b/resources/frontend_client/app/query_builder/query_builder.js
@@ -115,7 +115,8 @@ var QueryBuilder = React.createClass({
         });
 
         var saver,
-            result;
+            result,
+            download;
         if(this.props.model.result) {
             saver = (
                 <Saver
@@ -133,7 +134,10 @@ var QueryBuilder = React.createClass({
                     result={this.props.model.result}
                     setDisplay={this.props.model.setDisplay.bind(this.props.model)}
                 />
-            )
+            );
+            download = (
+                <a className="ActionButton inline-block mr1" href={this.props.model.getDownloadLink()} target="_blank">Download data</a>
+            );
         }
 
 
@@ -187,6 +191,7 @@ var QueryBuilder = React.createClass({
 
                     <div className="ActionBar">
                         {saver}
+                        {download}
                     </div>
             </div>
         )
diff --git a/resources/frontend_client/index.html b/resources/frontend_client/index.html
index d3e339d42da34e80b440b685fa0e194952cbfa6f..f5af42d33af2c1171c164c3eae0a12c300a7a72c 100644
--- a/resources/frontend_client/index.html
+++ b/resources/frontend_client/index.html
@@ -165,16 +165,16 @@
 
 <!-- JS INCLUDES -->
 
-  <script src="/app/bower_components/react/react-with-addons.js"></script>
-  <script src="/app/bower_components/react-onclickoutside/index.js"></script>
-  <script src="/app/bower_components/moment/min/moment.min.js"></script>
-  <script src="/app/bower_components/tether/tether.min.js"></script>
-  <script src="/app/bower_components/react-date-picker/react-datepicker.js"></script>
+<!-- vendor js -->
+<script src="/app/bower_components/react/react-with-addons.min.js"></script>
+<script src="/app/bower_components/react-onclickoutside/index.js"></script>
+<script src="/app/bower_components/moment/min/moment.min.js"></script>
+<script src="/app/bower_components/tether/tether.min.js"></script>
+<script src="/app/bower_components/react-date-picker/react-datepicker.js"></script>
 
-  <script src="/app/dist/query_builder.js" type="text/javascript"></script>
 
 <script src="/app/bower_components/jquery/dist/jquery.min.js"></script>
-<script src="/app/bower_components/underscore/underscore.js"></script>
+<script src="/app/bower_components/underscore/underscore-min.js"></script>
 <script src="/app/bower_components/fastclick/lib/fastclick.js"></script>
 
 <script src="/app/bower_components/angular-route/angular-route.min.js"></script>
@@ -182,16 +182,16 @@
 <script src="/app/bower_components/angular-cookies/angular-cookies.min.js"></script>
 <script src="/app/bower_components/angular-animate/angular-animate.min.js"></script>
 <script src="/app/bower_components/angular-sanitize/angular-sanitize.min.js"></script>
-<script src="/app/bower_components/angular-xeditable/dist/js/xeditable.js"></script>
+<script src="/app/bower_components/angular-xeditable/dist/js/xeditable.min.js"></script>
 <script src="/app/bower_components/angular-cookie/angular-cookie.min.js"></script>
 <script src="/app/bower_components/javascript-detect-element-resize/detect-element-resize.js"></script>
-<script src="/app/bower_components/angular-gridster/src/angular-gridster.js"></script>
+<script src="/app/bower_components/angular-gridster/dist/angular-gridster.min.js"></script>
 <script src="/app/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
 <script src="/app/bower_components/angular-http-auth/src/http-auth-interceptor.js"></script>
 <script src="/app/bower_components/ace-builds/src-min-noconflict/ace.js"></script>
 <script src="/app/bower_components/ace-builds/src-min-noconflict/mode-sql.js"></script>
 <script src="/app/bower_components/ace-builds/src-min-noconflict/ext-language_tools.js"></script>
-<script src="/app/bower_components/angular-ui-ace/ui-ace.js"></script>
+<script src="/app/bower_components/angular-ui-ace/ui-ace.min.js"></script>
 <script src="/app/bower_components/angularytics/dist/angularytics.min.js"></script>
 <script src="/app/bower_components/ng-sortable/dist/ng-sortable.min.js"></script>
 <script src="/app/bower_components/angular-readable-time/angular-readable-time.min.js"></script>
@@ -202,83 +202,9 @@
 
 <script src="/app/js/google_maps.js"></script>
 
-<!-- global app -->
-<script src="/app/app.js"></script>
-<script src="/app/services.js"></script>
-<script src="/app/controllers.js"></script>
-<script src="/app/filters.js"></script>
-<script src="/app/directives.js"></script>
-<script src="/app/auth/auth.module.js"></script>
-<script src="/app/auth/auth.controllers.js"></script>
-
-<!-- components and shared bits -->
-
-<script src="/app/annotation/annotation.services.js"></script>
-<script src="/app/annotation/annotation.directives.js"></script>
-<script src="/app/metabase/metabase.services.js"></script>
-
-<script src="/app/components/components.module.js"></script>
-<script src="/app/components/core_nav/core_nav.js"></script>
-<script src="/app/components/workspace/workspace.js"></script>
-<script src="/app/components/icons/icons.js"></script>
-
-<!-- superadmin section -->
-<script src="/app/superadmin/index/index.module.js"></script>
-<script src="/app/superadmin/index/index.controllers.js"></script>
-<script src="/app/superadmin/index/index.services.js"></script>
-<script src="/app/superadmin/organization/organization.module.js"></script>
-<script src="/app/superadmin/organization/organization.controllers.js"></script>
-
-<!-- admin section -->
-<script src="/app/admin/admin.controllers.js"></script>
-<script src="/app/admin/databases/databases.module.js"></script>
-<script src="/app/admin/databases/databases.controllers.js"></script>
-<script src="/app/admin/datasets/datasets.module.js"></script>
-<script src="/app/admin/datasets/datasets.controllers.js"></script>
-<script src="/app/admin/datasets/datasets.directives.js"></script>
-<script src="/app/admin/emailreport/emailreport.module.js"></script>
-<script src="/app/admin/emailreport/emailreport.controllers.js"></script>
-<script src="/app/admin/emailreport/emailreport.services.js"></script>
-<script src="/app/admin/people/people.module.js"></script>
-<script src="/app/admin/people/people.controllers.js"></script>
-<script src="/app/admin/people/people.directives.js"></script>
-<script src="/app/admin/query/query.module.js"></script>
-<script src="/app/admin/query/query.controllers.js"></script>
-<script src="/app/admin/query/query.services.js"></script>
-<script src="/app/admin/annotation/annotation.module.js"></script>
-<script src="/app/admin/annotation/annotation.controllers.js"></script>
-<script src="/app/admin/search/search.module.js"></script>
-<script src="/app/admin/search/search.controllers.js"></script>
-
-<!-- main app -->
-<script src="/app/card/card.module.js"></script>
-<script src="/app/card/card.controllers.js"></script>
-<script src="/app/card/card.services.js"></script>
-<script src="/app/card/card.directives.js"></script>
-<script src="/app/card/card.charting.js"></script>
-<script src="/app/dashboard/dashboard.module.js"></script>
-<script src="/app/dashboard/dashboard.controllers.js"></script>
-<script src="/app/dashboard/dashboard.services.js"></script>
-<script src="/app/dashboard/dashboard.directives.js"></script>
-<script src="/app/explore/explore.module.js"></script>
-<script src="/app/explore/explore.controllers.js"></script>
-<script src="/app/explore/explore.directives.js"></script>
-<script src="/app/explore/explore.services.js"></script>
-<script src="/app/user/user.module.js"></script>
-<script src="/app/user/user.controllers.js"></script>
-<script src="/app/user/user.directives.js"></script>
-<script src="/app/search/search.module.js"></script>
-<script src="/app/search/search.controllers.js"></script>
-<script src="/app/search/search.services.js"></script>
-
-<!-- custom shtuff:  be wary -->
-<script src="/app/operator/operator.module.js"></script>
-<script src="/app/operator/operator.controllers.js"></script>
-<script src="/app/operator/operator.services.js"></script>
-
-<script src="/app/reserve/reserve.module.js"></script>
-<script src="/app/reserve/reserve.controllers.js"></script>
-<script src="/app/reserve/reserve.services.js"></script>
+<!-- dist app -->
+<script src="/app/dist/app.js"></script>
+<script src="/app/dist/query_builder.js" type="text/javascript"></script>
 
 <script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
 <script>
diff --git a/src/metabase/util.clj b/src/metabase/util.clj
index 5f02043788b749dd0f0410f3a26a8eef1aaad0ba..ee8119c5fe7e4223514714ab1494f381c085b044 100644
--- a/src/metabase/util.clj
+++ b/src/metabase/util.clj
@@ -149,3 +149,13 @@
     false
     (boolean (re-matches #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
                          (clojure.string/lower-case v)))))
+
+(defn rpartial
+  "Like `partial`, but applies additional args *before* BOUND-ARGS.
+   Inspired by [`-rpartial` from dash.el](https://github.com/magnars/dash.el#-rpartial-fn-rest-args)
+
+    ((partial - 5) 8)  -> (- 5 8) -> -3
+    ((rpartial - 5) 8) -> (- 8 5) -> 3"
+  [f & bound-args]
+  (fn [& args]
+    (apply f (concat args bound-args))))
diff --git a/test/metabase/test_data/data.clj b/test/metabase/test_data/data.clj
index 779590b358272dcdeba90c1b0553a728f393b81d..a7c6b86bd3e390e32943dc3636d59e1a9d517233 100644
--- a/test/metabase/test_data/data.clj
+++ b/test/metabase/test_data/data.clj
@@ -12,21 +12,21 @@
 
 ;; [name last_login]
 (defonce ^:private users
-  [["Justin Ho" (timestamp 2014 4 1 1 30)]
-   ["David Baden" (timestamp 2014 12 5 7 15)]
-   ["Roberto Sanabria" (timestamp 2014 11 6 8 15)]
-   ["Noah Sidman-Gale" (timestamp 2014 1 1 0 30)]
-   ["Cam Saül" (timestamp 2014 10 3 10 30)]
-   ["Kyle Doherty" (timestamp 2014 8 2 5 30)]
-   ["Anshu Agarwal" (timestamp 2014 8 2 2 30)]
-   ["Kiah Buchner" (timestamp 2014 2 1 2 15)]
-   ["Adam Stocker" (timestamp 2014 4 3 2 30)]
-   ["Allen Gilliland" (timestamp 2014 7 3 12 30)]
-   ["Christina Crosetti" (timestamp 2014 11 1)]
-   ["Sameer Al-Sakran" (timestamp 2014 7 2 18 30)]
-   ["Daniel Wiesenthal" (timestamp 2014 8 1 3 30)]
-   ["Douglas Graves" (timestamp 2014 10 3 6 45)]
-   ["RoveLine Ibañez" (timestamp 2014 8 1 5 45)]])
+  [["Plato Yeshua" (timestamp 2014 4 1 1 30)]
+   ["Felipinho Asklepios" (timestamp 2014 12 5 7 15)]
+   ["Kaneonuskatew Eiran" (timestamp 2014 11 6 8 15)]
+   ["Simcha Yan" (timestamp 2014 1 1 0 30)]
+   ["Quentin Sören" (timestamp 2014 10 3 10 30)]
+   ["Shad Ferdynand" (timestamp 2014 8 2 5 30)]
+   ["Conchúr Tihomir" (timestamp 2014 8 2 2 30)]
+   ["Szymon Theutrich" (timestamp 2014 2 1 2 15)]
+   ["Nils Gotam" (timestamp 2014 4 3 2 30)]
+   ["Frans Hevel" (timestamp 2014 7 3 12 30)]
+   ["Spiros Teofil" (timestamp 2014 11 1)]
+   ["Kfir Caj" (timestamp 2014 7 2 18 30)]
+   ["Dwight Gresham" (timestamp 2014 8 1 3 30)]
+   ["Broen Olujimi" (timestamp 2014 10 3 6 45)]
+   ["Rüstem Hebel" (timestamp 2014 8 1 5 45)]])
 
 ;; name
 (defonce ^:private categories
diff --git a/test/metabase/test_util.clj b/test/metabase/test_util.clj
index 331b5b165d1ce9058da47ec56356bb2aa462e9b9..b22b480878aacb842dc2b865c3d999dd6e83c66c 100644
--- a/test/metabase/test_util.clj
+++ b/test/metabase/test_util.clj
@@ -28,3 +28,12 @@
           :a 100
           :b (+ 100 (:a <>))
           :c (+ 100 (:b <>))))
+
+
+;;; tests for RPARTIAL
+
+(expect 3
+  ((rpartial - 5) 8))
+
+(expect -7
+  ((rpartial - 5 10) 8))