Skip to content
Browse files

Implement a grunt-based asset build process.

mas grunt
  • Loading branch information...
1 parent 4d67924 commit 626bac4a249be68d6b09c25727d1b8b44ff552b7 @ajb ajb committed Nov 20, 2012
Showing with 16,187 additions and 7,237 deletions.
  1. +0 −70 application/assets.php
  2. +0 −4 application/bundles.php
  3. +5 −0 application/config/assets.php
  4. +5 −0 application/config/ec2/assets.php
  5. +5 −0 application/config/production/assets.php
  6. +5 −0 application/config/staging/assets.php
  7. +32 −20 application/libraries/helper.php
  8. +3 −2 application/start.php
  9. +0 −10 application/tasks/compile_assets.php
  10. +9 −0 application/tasks/increment_deploy_timestamp.php
  11. +1 −6 application/views/bids/review.jade
  12. +1 −5 application/views/bids/review.php
  13. +3 −16 application/views/layout.jade
  14. +3 −13 application/views/layout.php
  15. +1 −0 assets/build/.gitignore
  16. +133 −0 assets/build/grunt.js
  17. +12 −0 assets/build/package.json
  18. 0 {public → assets}/coffee/admin-officers-backbone.coffee
  19. 0 {public → assets}/coffee/admin-projects-backbone.coffee
  20. 0 {public → assets}/coffee/bid-review.coffee
  21. 0 {public → assets}/coffee/collaborators-backbone.coffee
  22. 0 {public → assets}/coffee/collaborators.coffee
  23. 0 {public → assets}/coffee/comments-backbone.coffee
  24. 0 {public → assets}/coffee/dsbs-lookup.coffee
  25. 0 {public → assets}/coffee/filter-projects.coffee
  26. 0 {public → assets}/coffee/flash-button.coffee
  27. 0 {public → assets}/coffee/infinite-vendor-scroll.coffee
  28. 0 {public → assets}/coffee/main.coffee
  29. 0 {public → assets}/coffee/new-bid.coffee
  30. 0 {public → assets}/coffee/notifications.coffee
  31. 0 {public → assets}/coffee/question-and-answer.coffee
  32. 0 {public → assets}/coffee/save-bid-draft.coffee
  33. 0 {public → assets}/coffee/sow-composer.coffee
  34. 0 {public → assets}/coffee/sow-deliverables-backbone.coffee
  35. 0 {public → assets}/coffee/validation.coffee
  36. 0 {public → assets}/coffee/vendor-image-preview.coffee
  37. +0 −10 {public → assets}/css/bootstrap-responsive.css
  38. 0 {public → assets}/css/bootstrap-wysihtml5.css
  39. 0 {public → assets}/css/bootstrap.css
  40. 0 public/css/main.css → assets/css/compiled_styl.css
  41. 0 {public → assets}/css/datepicker.css
  42. 0 {public → assets}/js/.gitignore
  43. +93 −0 assets/js/admin-officers-backbone.js
  44. +76 −0 assets/js/admin-projects-backbone.js
  45. +296 −0 assets/js/bid-review.js
  46. +121 −0 assets/js/collaborators-backbone.js
  47. +30 −0 assets/js/collaborators.js
  48. +105 −0 assets/js/comments-backbone.js
  49. +34 −0 assets/js/dsbs-lookup.js
  50. +13 −0 assets/js/filter-projects.js
  51. +23 −0 assets/js/flash-button.js
  52. +47 −0 assets/js/infinite-vendor-scroll.js
  53. +59 −0 assets/js/main.js
  54. +34 −0 assets/js/new-bid.js
  55. +102 −0 assets/js/notifications.js
  56. +76 −0 assets/js/question-and-answer.js
  57. +30 −0 assets/js/save-bid-draft.js
  58. +347 −0 assets/js/sow-composer.js
  59. +132 −0 assets/js/sow-deliverables-backbone.js
  60. +136 −0 assets/js/validation.js
  61. +35 −0 assets/js/vendor-image-preview.js
  62. 0 {public → assets}/js/vendor/autogrow-input.js
  63. 0 {public → assets}/js/vendor/backbone.js
  64. 0 {public → assets}/js/vendor/bootstrap-datepicker.js
  65. 0 {public → assets}/js/vendor/bootstrap-wysihtml5.js
  66. 0 {public → assets}/js/vendor/bootstrap.js
  67. 0 {public → assets}/js/vendor/google.analytics.js
  68. 0 {public → assets}/js/vendor/jquery-1.8.1.min.js
  69. 0 {public → assets}/js/vendor/jquery.form.js
  70. 0 {public → assets}/js/vendor/jquery.formtimer.js
  71. 0 {public → assets}/js/vendor/jquery.hotkeys.js
  72. 0 {public → assets}/js/vendor/jquery.pjax.js
  73. 0 {public → assets}/js/vendor/jquery.sortable.js
  74. 0 {public → assets}/js/vendor/jquery.timeago.js
  75. 0 {public → assets}/js/vendor/jquery.validate.js
  76. 0 {public → assets}/js/vendor/jquery.validate_rfpez.js
  77. 0 {public → assets}/js/vendor/underscore.js
  78. 0 {public → assets}/js/vendor/wysihtml5.min.js
  79. 0 {public/css → assets/styl}/components/bids.styl
  80. 0 {public/css → assets/styl}/components/comments.styl
  81. 0 {public/css → assets/styl}/components/dsbs-certifications.styl
  82. 0 {public/css → assets/styl}/components/nav.styl
  83. 0 {public/css → assets/styl}/components/notifications-dropdown.styl
  84. 0 {public/css → assets/styl}/components/question-and-answer.styl
  85. 0 {public/css → assets/styl}/components/vendors.styl
  86. 0 {public/css → assets/styl}/globals/conditional.styl
  87. 0 {public/css → assets/styl}/globals/custom_methods.styl
  88. 0 {public/css → assets/styl}/globals/globals.styl
  89. +1 −3 {public/css → assets/styl}/main.styl
  90. 0 {public/css → assets/styl}/sections/admin.styl
  91. 0 {public/css → assets/styl}/sections/bids.styl
  92. 0 {public/css → assets/styl}/sections/notifications.styl
  93. 0 {public/css → assets/styl}/sections/projects.styl
  94. 0 {public/css → assets/styl}/sections/registration.styl
  95. 0 {public/css → assets/styl}/sections/sow-composer.styl
  96. 0 {public/css → assets/styl}/sections/vendors.styl
  97. +0 −12 bundles/basset/LICENSE
  98. +0 −78 bundles/basset/README.md
  99. +0 −2 bundles/basset/compiled/.gitignore
  100. +0 −89 bundles/basset/config/basset.php
  101. +0 −221 bundles/basset/libraries/core/asset.php
  102. +0 −189 bundles/basset/libraries/core/basset.php
  103. +0 −111 bundles/basset/libraries/core/cache.php
  104. +0 −75 bundles/basset/libraries/core/config.php
  105. +0 −571 bundles/basset/libraries/core/container.php
  106. +0 −264 bundles/basset/libraries/vendor/csscompress.php
  107. +0 −359 bundles/basset/libraries/vendor/jsmin.php
  108. +0 −2,691 bundles/basset/libraries/vendor/less.php
  109. +0 −291 bundles/basset/libraries/vendor/urirewriter.php
  110. +0 −38 bundles/basset/routes.php
  111. +0 −68 bundles/basset/start.php
  112. +0 −153 bundles/basset/tests/basset.test.php
  113. +1 −0 deploy_timestamp.txt
  114. +1 −1 post_deploy.sh
  115. 0 public/bundles/.gitignore
  116. +2,742 −0 public/css/all.css
  117. +2 −0 public/css/all.min.css
  118. +0 −97 public/js/admin-officers-backbone.js
  119. +0 −80 public/js/admin-projects-backbone.js
  120. +11,431 −0 public/js/all.js
  121. +2 −0 public/js/all.min.js
  122. +0 −300 public/js/bid-review.js
  123. +0 −125 public/js/collaborators-backbone.js
  124. +0 −34 public/js/collaborators.js
  125. +0 −109 public/js/comments-backbone.js
  126. +0 −38 public/js/dsbs-lookup.js
  127. +0 −17 public/js/filter-projects.js
  128. +0 −27 public/js/flash-button.js
  129. +0 −51 public/js/infinite-vendor-scroll.js
  130. +0 −63 public/js/main.js
  131. 0 public/js/{vendor/modernizr-2.6.1-respond-1.1.0.min.js → modernizr.js}
  132. +0 −38 public/js/new-bid.js
  133. +0 −106 public/js/notifications.js
  134. +0 −80 public/js/question-and-answer.js
  135. +0 −34 public/js/save-bid-draft.js
  136. +0 −351 public/js/sow-composer.js
  137. +0 −136 public/js/sow-deliverables-backbone.js
  138. +0 −140 public/js/validation.js
  139. +0 −39 public/js/vendor-image-preview.js
View
70 application/assets.php
@@ -1,70 +0,0 @@
-<?php
-
-/*
-|--------------------------------------------------------------------------
-| Asset Definitions
-|--------------------------------------------------------------------------
-*/
-
-Bundle::start('basset');
-
-if (Config::get('basset')) Basset\Config::extend(Config::get('basset'));
-
-Basset::scripts('global', function ($b){
- $b->add('js/vendor/bootstrap.js')
- ->add('js/vendor/jquery.validate.js')
- ->add('js/vendor/jquery.validate_rfpez.js')
- ->add('js/vendor/jquery.timeago.js')
- ->add('js/vendor/jquery.form.js')
- ->add('js/vendor/jquery.pjax.js')
- ->add('js/flash-button.js')
- ->add('js/main.js')
- ->add('js/question-and-answer.js')
- ->add('js/validation.js')
- ->add('js/filter-projects.js')
- ->add('js/notifications.js')
- ->add('js/dsbs-lookup.js')
- ->add('js/infinite-vendor-scroll.js')
- ->add('js/vendor/underscore.js')
- ->add('js/vendor/backbone.js')
- ->compress();
-});
-
-Basset::scripts('vendor', function ($b){
- $b->add('js/vendor-image-preview.js')
- ->add('js/new-bid.js')
- ->add('js/save-bid-draft.js')
- ->compress();
-});
-
-Basset::scripts('officer', function ($b){
- $b->add('js/vendor/bootstrap-datepicker.js')
- ->add('js/vendor/wysihtml5.min.js')
- ->add('js/vendor/bootstrap-wysihtml5.js')
- ->add('js/vendor/jquery.sortable.js')
- ->add('js/vendor/autogrow-input.js')
- ->add('js/vendor/jquery.hotkeys.js')
- ->add('js/collaborators.js')
- ->add('js/sow-composer.js')
- ->add('js/comments-backbone.js')
- ->add('js/collaborators-backbone.js')
- ->add('js/sow-deliverables-backbone.js')
- ->compress();
-});
-
-Basset::scripts('admin', function ($b){
- $b->add('js/admin-officers-backbone.js')
- ->add('js/admin-projects-backbone.js')
- ->compress();
-});
-
-
-Basset::styles('website', function($basset)
-{
- $basset->add('bootstrap', 'bootstrap.css')
- ->add('bootstrap-responsive', 'bootstrap-responsive.css')
- ->add('bootstrap-wysihtml5', 'bootstrap-wysihtml5.css')
- ->add('datepicker', 'datepicker.css')
- ->add('maincss', 'main.css')
- ->compress();
-});
View
4 application/bundles.php
@@ -36,10 +36,6 @@
return array(
'bob',
- 'basset' => array(
- 'handles' => 'basset',
- 'auto' => true
- ),
'jaded' => array(
'auto' => true
),
View
5 application/config/assets.php
@@ -0,0 +1,5 @@
+<?php
+
+return array(
+ 'use_minified' => false
+);
View
5 application/config/ec2/assets.php
@@ -0,0 +1,5 @@
+<?php
+
+return array(
+ 'use_minified' => true
+);
View
5 application/config/production/assets.php
@@ -0,0 +1,5 @@
+<?php
+
+return array(
+ 'use_minified' => true
+);
View
5 application/config/staging/assets.php
@@ -0,0 +1,5 @@
+<?php
+
+return array(
+ 'use_minified' => true
+);
View
52 application/libraries/helper.php
@@ -2,30 +2,42 @@
Class Helper {
-public static function timeago($timestamp) {
- $str = strtotime($timestamp);
- return "<span class='timeago' title='".date('c', $str)."'>".date('r', $str)."</abbr>";
-}
+ public static function asset($n) {
-public static function helper_tooltip($title, $placement = "top", $pull_right = false) {
- return "<span class='helper-tooltip ".($pull_right ? 'pull-right' : '')."' data-title=\"".htmlspecialchars($title)."\" data-trigger='manual' data-placement='$placement'>
- <i class='icon-question-sign icon-white'></i>
- </span>";
-}
+ if (preg_match('/^css/', $n)) {
+ $ext = Config::get('assets.use_minified') === false ? ".css" : ".min.css?t=".Config::get('deploy_timestamp');
+ return HTML::style($n.$ext);
+ } elseif (preg_match('/^js/', $n)) {
+ $ext = Config::get('assets.use_minified') === false ? ".js" : ".min.js?t=".Config::get('deploy_timestamp');
+ return HTML::script($n.$ext);
+ } else {
+ throw new \Exception("Can't handle that asset type.");
+ }
+ }
-public static function datum($label, $content, $link = false) {
- if ($content) {
- $isEmail = filter_var($content, FILTER_VALIDATE_EMAIL);
- return "<div class='datum'>
- <label>$label</label>
- <div class='content'>".($link ? "<a href='".($isEmail ? "mailto:$content" : $content).
- "' ".($isEmail ? '' : 'target="_blank"').">" : "")."$content".($link ? '</a>' : '')."</div>
- </div>";
- } else {
- return '';
+ public static function timeago($timestamp) {
+ $str = strtotime($timestamp);
+ return "<span class='timeago' title='".date('c', $str)."'>".date('r', $str)."</abbr>";
}
-}
+ public static function helper_tooltip($title, $placement = "top", $pull_right = false) {
+ return "<span class='helper-tooltip ".($pull_right ? 'pull-right' : '')."' data-title=\"".htmlspecialchars($title)."\" data-trigger='manual' data-placement='$placement'>
+ <i class='icon-question-sign icon-white'></i>
+ </span>";
+ }
+
+ public static function datum($label, $content, $link = false) {
+ if ($content) {
+ $isEmail = filter_var($content, FILTER_VALIDATE_EMAIL);
+ return "<div class='datum'>
+ <label>$label</label>
+ <div class='content'>".($link ? "<a href='".($isEmail ? "mailto:$content" : $content).
+ "' ".($isEmail ? '' : 'target="_blank"').">" : "")."$content".($link ? '</a>' : '')."</div>
+ </div>";
+ } else {
+ return '';
+ }
+ }
public static function to_array($results) {
$return_array = array();
View
5 application/start.php
@@ -187,10 +187,11 @@
Session::load();
}
-require 'assets.php';
-
+// @todo move this into a config file
if (Request::is_env('production')) Config::set('error.detail', false);
+Config::set('deploy_timestamp', file_get_contents('deploy_timestamp.txt'));
+
Auth::extend('rfpez', function(){
return new RfpezAuth;
});
View
10 application/tasks/compile_assets.php
@@ -1,10 +0,0 @@
-<?php
-
-class Compile_Assets_Task {
-
- public function run()
- {
- Basset::compile();
- }
-
-}
View
9 application/tasks/increment_deploy_timestamp.php
@@ -0,0 +1,9 @@
+<?php
+
+class Increment_Deploy_Timestamp_Task extends Task {
+
+ public function run() {
+ print file_put_contents("deploy_timestamp.txt", time());
+ }
+
+}
View
7 application/views/bids/review.jade
@@ -8,17 +8,12 @@
!{View::make('bids.partials.dismiss_modal')}
!{View::make('bids.partials.award_modal')->with('project', $project)}
-- Section::start('page_scripts');
- !{HTML::script('js/bid-review.js')}
-- Section::stop();
-
-
.well
a#review-tips-toggle(data-hide-text="Hide Tips [-]") Show Tips [+]
#review-tips.collapse
ul
- li Stars are shared among collaborators. By starring a bid, you can indicate to your colleagues you think that bid stands out.
+ li Stars are shared among collaborators. By starring a bid, you can indicate to your colleagues that you think a bid stands out.
div(class="winning-bid-table-wrapper #{$project->winning_bid() ? '' : 'hide'}")
View
6 application/views/bids/review.php
@@ -6,15 +6,11 @@
<?php echo Jade\Dumper::_html(View::make('projects.partials.toolbar')->with('project', $project)); ?>
<?php echo Jade\Dumper::_html(View::make('bids.partials.dismiss_modal')); ?>
<?php echo Jade\Dumper::_html(View::make('bids.partials.award_modal')->with('project', $project)); ?>
-<?php Section::start('page_scripts'); { ?>
- <?php echo Jade\Dumper::_html(HTML::script('js/bid-review.js')); ?>
-<?php } ?>
-<?php Section::stop(); ?>
<div class="well">
<a id="review-tips-toggle" data-hide-text="Hide Tips [-]">Show Tips [+]</a>
<div id="review-tips" class="collapse">
<ul>
- <li>Stars are shared among collaborators. By starring a bid, you can indicate to your colleagues you think that bid stands out.</li>
+ <li>Stars are shared among collaborators. By starring a bid, you can indicate to your colleagues that you think a bid stands out.</li>
</ul>
</div>
</div>
View
19 application/views/layout.jade
@@ -15,7 +15,7 @@ head
title #{Helper::full_title(Section::yield('page_title'), Section::yield('page_action'))}
!{HTML::style('http://fonts.googleapis.com/css?family=Telex')}
- !{Basset::show('website.css')}
+ !{Helper::asset('css/all')}
- if (Auth::guest()):
@@ -32,26 +32,13 @@ head
- $body_class .= " super-admin"
- endif;
- !{HTML::script('js/vendor/modernizr-2.6.1-respond-1.1.0.min.js')}
+ !{HTML::script('js/modernizr.js')}
script(src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js")
script
| window.jQuery || document.write('<script src="/js/vendor/jquery-1.8.1.min.js"><\/script>')
- !{Basset::show('global.js')}
-
- - if (Auth::user()):
- - if (Auth::officer() && Auth::officer()->is_role_or_higher(Officer::ROLE_ADMIN)):
- !{Basset::show('admin.js')}
- - endif;
-
- - if (Auth::officer()):
- !{Basset::show('officer.js')}
- - else:
- !{Basset::show('vendor.js')}
- - endif;
- - endif;
-
+ !{Helper::asset('js/all')}
!{Section::yield('additional_scripts')}
View
16 application/views/layout.php
@@ -11,7 +11,7 @@
<title><?php echo Jade\Dumper::_text(Helper::full_title(Section::yield('page_title'), Section::yield('page_action'))); ?>
</title>
<?php echo Jade\Dumper::_html(HTML::style('http://fonts.googleapis.com/css?family=Telex')); ?>
- <?php echo Jade\Dumper::_html(Basset::show('website.css')); ?>
+ <?php echo Jade\Dumper::_html(Helper::asset('css/all')); ?>
<?php if (Auth::guest()): ?>
<?php $body_class = "no-auth"; ?>
<?php print "<style>.only-user { display: none; }</style>"; ?>
@@ -24,22 +24,12 @@
<?php elseif (Auth::officer() && Auth::officer()->role == Officer::ROLE_SUPER_ADMIN): ?>
<?php $body_class .= " super-admin" ?>
<?php endif; ?>
- <?php echo Jade\Dumper::_html(HTML::script('js/vendor/modernizr-2.6.1-respond-1.1.0.min.js')); ?>
+ <?php echo Jade\Dumper::_html(HTML::script('js/modernizr.js')); ?>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="/js/vendor/jquery-1.8.1.min.js"><\/script>')
</script>
- <?php echo Jade\Dumper::_html(Basset::show('global.js')); ?>
- <?php if (Auth::user()): ?>
- <?php if (Auth::officer() && Auth::officer()->is_role_or_higher(Officer::ROLE_ADMIN)): ?>
- <?php echo Jade\Dumper::_html(Basset::show('admin.js')); ?>
- <?php endif; ?>
- <?php if (Auth::officer()): ?>
- <?php echo Jade\Dumper::_html(Basset::show('officer.js')); ?>
- <?php else: ?>
- <?php echo Jade\Dumper::_html(Basset::show('vendor.js')); ?>
- <?php endif; ?>
- <?php endif; ?>
+ <?php echo Jade\Dumper::_html(Helper::asset('js/all')); ?>
<?php echo Jade\Dumper::_html(Section::yield('additional_scripts')); ?>
</head>
<body class="<?php echo Jade\Dumper::_text($body_class); ?>">
View
1 assets/build/.gitignore
@@ -0,0 +1 @@
+node_modules
View
133 assets/build/grunt.js
@@ -0,0 +1,133 @@
+module.exports = function(grunt) {
+
+ var tasks = 'stylus jader coffee:all concat cssmin:all min:js';
+
+ var path = require('path');
+ var exec = require('child_process').exec;
+
+ grunt.loadNpmTasks('grunt-css');
+ grunt.loadNpmTasks('grunt-coffee');
+ grunt.loadNpmTasks('grunt-contrib-stylus');
+
+ grunt.registerTask('jader', 'Compiles jade templates to PHP.', function() {
+ var cb = this.async();
+
+ var child = exec('php ../../artisan jader', function (error, stdout, stderr) {
+ console.log(error ? error : "Done");
+ cb();
+ });
+ });
+
+
+ grunt.initConfig({
+
+ pkg: '<json:package.json>',
+ meta: {
+ banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
+ '<%= grunt.template.today("yyyy-mm-dd") %> */'
+ },
+
+ coffee: {
+ all: {
+ src: ['../coffee/**/*.coffee'],
+ dest: '../js',
+ options: {bare: true}
+ }
+ },
+
+ stylus: {
+ compile: {
+ files: {
+ '../css/compiled_styl.css': ['../styl/main.styl']
+ }
+ },
+ },
+
+
+ concat: {
+ css: {
+ src: [
+ '../css/bootstrap.css',
+ '../css/bootstrap-responsive.css',
+ '../css/bootstrap-wysihtml5.css',
+ '../css/bootstrap-datepicker.css',
+ '../css/compiled_styl.css'
+ ],
+ dest: '../../public/css/all.css'
+ },
+
+ js: {
+ src: [
+ // global
+ '../js/vendor/bootstrap.js',
+ '../js/vendor/jquery.validate.js',
+ '../js/vendor/jquery.validate_rfpez.js',
+ '../js/vendor/jquery.timeago.js',
+ '../js/vendor/jquery.form.js',
+ '../js/vendor/jquery.pjax.js',
+ '../js/flash-button.js',
+ '../js/main.js',
+ '../js/question-and-answer.js',
+ '../js/validation.js',
+ '../js/filter-projects.js',
+ '../js/notifications.js',
+ '../js/dsbs-lookup.js',
+ '../js/infinite-vendor-scroll.js',
+ '../js/vendor/underscore.js',
+ '../js/vendor/backbone.js',
+
+
+ // vendor
+ '../js/vendor-image-preview.js',
+ '../js/new-bid.js',
+ '../js/save-bid-draft.js',
+
+ // officer
+ '../js/vendor/bootstrap-datepicker.js',
+ '../js/vendor/wysihtml5.min.js',
+ '../js/vendor/bootstrap-wysihtml5.js',
+ '../js/vendor/jquery.sortable.js',
+ '../js/vendor/autogrow-input.js',
+ '../js/vendor/jquery.hotkeys.js',
+ '../js/collaborators.js',
+ '../js/sow-composer.js',
+ '../js/comments-backbone.js',
+ '../js/collaborators-backbone.js',
+ '../js/sow-deliverables-backbone.js',
+ '../js/bid-review.js',
+
+ // admin
+ '../js/admin-officers-backbone.js',
+ '../js/admin-projects-backbone.js'
+
+ ],
+ dest: '../../public/js/all.js'
+ }
+ },
+
+ cssmin: {
+ all: {
+ src: ['<banner>', '../../public/css/all.css'],
+ dest: '../../public/css/all.min.css'
+ }
+ },
+
+ min: {
+ js: {
+ src: ['<banner>', '../../public/js/all.js'],
+ dest: '../../public/js/all.min.js'
+ }
+ },
+
+
+ watch: {
+ app: {
+ files: ['../coffee/**/*.coffee', '../styl/**/*.styl', '../css/**/*.css', '../js/**/*.js', '../../application/views/**/*.jade'],
+ tasks: tasks
+ }
+ }
+ });
+
+ grunt.registerTask('default', tasks);
+
+};
View
12 assets/build/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "rfpez-build",
+ "version": "0.0.1",
+ "main": "grunt.js",
+ "dependencies": {
+ "grunt": "*",
+ "grunt-growl": "*",
+ "grunt-css": "*",
+ "grunt-coffee": "*",
+ "grunt-contrib-stylus": "*"
+ }
+}
View
0 public/coffee/admin-officers-backbone.coffee → assets/coffee/admin-officers-backbone.coffee
File renamed without changes.
View
0 public/coffee/admin-projects-backbone.coffee → assets/coffee/admin-projects-backbone.coffee
File renamed without changes.
View
0 public/coffee/bid-review.coffee → assets/coffee/bid-review.coffee
File renamed without changes.
View
0 public/coffee/collaborators-backbone.coffee → assets/coffee/collaborators-backbone.coffee
File renamed without changes.
View
0 public/coffee/collaborators.coffee → assets/coffee/collaborators.coffee
File renamed without changes.
View
0 public/coffee/comments-backbone.coffee → assets/coffee/comments-backbone.coffee
File renamed without changes.
View
0 public/coffee/dsbs-lookup.coffee → assets/coffee/dsbs-lookup.coffee
File renamed without changes.
View
0 public/coffee/filter-projects.coffee → assets/coffee/filter-projects.coffee
File renamed without changes.
View
0 public/coffee/flash-button.coffee → assets/coffee/flash-button.coffee
File renamed without changes.
View
0 public/coffee/infinite-vendor-scroll.coffee → assets/coffee/infinite-vendor-scroll.coffee
File renamed without changes.
View
0 public/coffee/main.coffee → assets/coffee/main.coffee
File renamed without changes.
View
0 public/coffee/new-bid.coffee → assets/coffee/new-bid.coffee
File renamed without changes.
View
0 public/coffee/notifications.coffee → assets/coffee/notifications.coffee
File renamed without changes.
View
0 public/coffee/question-and-answer.coffee → assets/coffee/question-and-answer.coffee
File renamed without changes.
View
0 public/coffee/save-bid-draft.coffee → assets/coffee/save-bid-draft.coffee
File renamed without changes.
View
0 public/coffee/sow-composer.coffee → assets/coffee/sow-composer.coffee
File renamed without changes.
View
0 ...c/coffee/sow-deliverables-backbone.coffee → ...s/coffee/sow-deliverables-backbone.coffee
File renamed without changes.
View
0 public/coffee/validation.coffee → assets/coffee/validation.coffee
File renamed without changes.
View
0 public/coffee/vendor-image-preview.coffee → assets/coffee/vendor-image-preview.coffee
File renamed without changes.
View
10 public/css/bootstrap-responsive.css → assets/css/bootstrap-responsive.css
@@ -1,13 +1,3 @@
-/*!
- * Bootstrap Responsive v2.2.1
- *
- * Copyright 2012 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world @twitter by @mdo and @fat.
- */
-
.clearfix {
*zoom: 1;
}
View
0 public/css/bootstrap-wysihtml5.css → assets/css/bootstrap-wysihtml5.css
File renamed without changes.
View
0 public/css/bootstrap.css → assets/css/bootstrap.css
File renamed without changes.
View
0 public/css/main.css → assets/css/compiled_styl.css
File renamed without changes.
View
0 public/css/datepicker.css → assets/css/datepicker.css
File renamed without changes.
View
0 public/js/.gitignore → assets/js/.gitignore
File renamed without changes.
View
93 assets/js/admin-officers-backbone.js
@@ -0,0 +1,93 @@
+var App, AppView, Officer, OfficerList, OfficerView, Officers;
+
+Officer = Backbone.Model.extend({
+ validate: function(attrs) {},
+ defaults: function() {},
+ clear: function() {
+ return this.destroy();
+ }
+});
+
+OfficerList = Backbone.Collection.extend({
+ model: Officer,
+ url: "/officers"
+});
+
+OfficerView = Backbone.View.extend({
+ tagName: "tr",
+ template: _.template("<td><%= id %></td>\n<td><%= name %></td>\n<td><%= title %></td>\n<td><%= User.email %></td>\n<td>\n <div class=\"not-user-<%= User.id %>\">\n <% if (role == 3 && !isSuperAdmin) { %>\n This officer is a super-admin.\n <% } else { %>\n <select class=\"user_role_select\">\n <option value=\"0\" <% if(role == 0){ %>selected <% } %>>Program Officer</option>\n <option value=\"1\" <% if(role == 1){ %>selected <% } %>>Contracting Officer</option>\n <option value=\"2\" <% if(role == 2){ %>selected <% } %>>Admin</option>\n <% if (isSuperAdmin) { %>\n <option value=\"3\" <% if(role == 3){ %>selected <% } %>>Super Admin</option>\n <% } %>\n </select>\n <% } %>\n </div>\n <div class=\"only-user only-user-<%= User.id %>\">\n You're a <%= role_text %>.\n </div>\n</td>\n<td>\n <% if (role != 3){ %>\n <div class=\"super-admin-only\">\n <div class=\"not-user-<%= User.id %>\">\n <% if (!User.banned_at){ %>\n <a class=\"btn btn-danger ban-button btn-mini\">Ban Officer</a>\n <% } else { %>\n <a class=\"btn unban-button btn-mini\">Un-Ban Officer</a>\n <% } %>\n </div>\n </div>\n <% } %>\n</td>"),
+ events: {
+ "change .user_role_select": "update",
+ "click .ban-button": "ban",
+ "click .unban-button": "unban"
+ },
+ initialize: function() {
+ this.model.bind("change", this.render, this);
+ return this.model.bind("destroy", this.remove, this);
+ },
+ render: function() {
+ this.$el.html(this.template(_.extend(this.model.toJSON(), {
+ isSuperAdmin: App.options.isSuperAdmin
+ })));
+ return this;
+ },
+ ban: function() {
+ if (confirm('Are you sure you want to ban this officer? This could have unintended consequences if they were the only officer on a project.')) {
+ return this.model.save({
+ command: "ban"
+ });
+ }
+ },
+ unban: function() {
+ return this.model.save({
+ command: "unban"
+ });
+ },
+ update: function() {
+ return this.model.save({
+ role: this.$el.find(".user_role_select").val()
+ });
+ },
+ clear: function() {
+ return this.model.clear();
+ }
+});
+
+AppView = Backbone.View.extend({
+ initialize: function() {
+ Officers.bind('reset', this.reset, this);
+ return Officers.bind('all', this.render, this);
+ },
+ reset: function() {
+ $("#officers-tbody").html('');
+ return this.addAll();
+ },
+ render: function() {},
+ addOne: function(officer) {
+ var html, view;
+ view = new OfficerView({
+ model: officer
+ });
+ html = view.render().el;
+ return $("#officers-tbody").append(html);
+ },
+ addAll: function() {
+ return Officers.each(this.addOne);
+ }
+});
+
+App = {};
+
+Officers = {};
+
+Rfpez.Backbone.AdminOfficers = function(initialModels) {
+ var isSuperAdmin;
+ isSuperAdmin = $("body").hasClass('super-admin');
+ Officers = new OfficerList;
+ App = new AppView({
+ collection: Officers,
+ isSuperAdmin: isSuperAdmin
+ });
+ Officers.reset(initialModels);
+ return App;
+};
View
76 assets/js/admin-projects-backbone.js
@@ -0,0 +1,76 @@
+var App, AppView, Project, ProjectList, ProjectView, Projects;
+
+Project = Backbone.Model.extend({
+ validate: function(attrs) {},
+ defaults: function() {},
+ clear: function() {
+ return this.destroy();
+ }
+});
+
+ProjectList = Backbone.Collection.extend({
+ model: Project,
+ url: "/projects"
+});
+
+ProjectView = Backbone.View.extend({
+ tagName: "tr",
+ template: _.template("<td><%= id %></td>\n<td><%= title %></td>\n<td><%= fork_count %></td>\n<td>\n <select class=\"recommended-select\">\n <option value=\"1\" <% if (recommended == 1){ %>selected<% } %>>Yes</option>\n <option value=\"0\" <% if (recommended == 0){ %>selected<% } %>>No</option>\n </select>\n</td>\n<td>\n <select class=\"public-select\">\n <option value=\"1\" <% if (public == 1){ %>selected<% } %>>Yes</option>\n <option value=\"0\" <% if (public == 0){ %>selected<% } %>>No</option>\n </select>\n</td>\n<td><%= project_type.name %></td>"),
+ events: {
+ "change .recommended-select": "update",
+ "change .public-select": "update"
+ },
+ update: function() {
+ return this.model.save({
+ recommended: this.$el.find(".recommended-select").val(),
+ "public": this.$el.find(".public-select").val()
+ });
+ },
+ initialize: function() {
+ this.model.bind("change", this.render, this);
+ return this.model.bind("destroy", this.remove, this);
+ },
+ render: function() {
+ this.$el.html(this.template(this.model.toJSON()));
+ return this;
+ },
+ clear: function() {
+ return this.model.clear();
+ }
+});
+
+AppView = Backbone.View.extend({
+ initialize: function() {
+ Projects.bind('reset', this.reset, this);
+ return Projects.bind('all', this.render, this);
+ },
+ reset: function() {
+ $("#projects-tbody").html('');
+ return this.addAll();
+ },
+ render: function() {},
+ addOne: function(project) {
+ var html, view;
+ view = new ProjectView({
+ model: project
+ });
+ html = view.render().el;
+ return $("#projects-tbody").append(html);
+ },
+ addAll: function() {
+ return Projects.each(this.addOne);
+ }
+});
+
+App = {};
+
+Projects = {};
+
+Rfpez.Backbone.AdminProjects = function(initialModels) {
+ Projects = new ProjectList;
+ App = new AppView({
+ collection: Projects
+ });
+ Projects.reset(initialModels);
+ return App;
+};
View
296 assets/js/bid-review.js
@@ -0,0 +1,296 @@
+var dismiss_selection, keep_bid_in_view, mouseover_select_timeout, on_mouseover_select, open_selection, star_selection, toggle_unread_selection;
+
+$(document).on('shown', '#dismiss-modal', function() {
+ $(this).find("select").focus().val('');
+ return $(this).find("input[name=reason_other]").val('').hide();
+});
+
+$(document).on("change", "#dismiss-modal select", function() {
+ if ($(this).val() === "Other") {
+ return $("#dismiss-modal input[name=reason_other]").show();
+ } else {
+ return $("#dismiss-modal input[name=reason_other]").val('').hide();
+ }
+});
+
+$(document).on("click", "#review-tips-toggle", function() {
+ return $("#review-tips").collapse('toggle');
+});
+
+$(document).on("show", "#review-tips", function() {
+ $("#review-tips-toggle").data('show-text', $("#review-tips-toggle").text());
+ return $("#review-tips-toggle").text($("#review-tips-toggle").data('hide-text'));
+});
+
+$(document).on("hide", "#review-tips", function() {
+ return $("#review-tips-toggle").text($("#review-tips-toggle").data('show-text'));
+});
+
+$(document).on("click", ".bid-notification-td .mark-as-read, .bid-notification-td .mark-as-unread", function() {
+ var action, bid, bid_id, el;
+ el = $(this);
+ bid = el.closest(".bid");
+ bid_id = bid.data('bid-id');
+ action = el.hasClass('mark-as-read') ? "read" : "unread";
+ Rfpez.view_notification_payload("bid", bid_id, action);
+ if (action === "read") {
+ return bid.removeClass('unread');
+ } else {
+ return bid.addClass('unread');
+ }
+});
+
+$(document).on("click", ".bid .unstar-button, .bid .star-button", function() {
+ var action, bid;
+ action = $(this).hasClass('unstar-button') ? "0" : "1";
+ bid = $(this).closest(".bid");
+ return $.ajax({
+ url: "/projects/" + bid.data('project-id') + "/bids/" + bid.data('bid-id') + "/star",
+ data: {
+ starred: action
+ },
+ type: "GET",
+ success: function(data) {
+ if (data.starred === '0') {
+ return bid.find(".star-td").removeClass("starred");
+ } else {
+ return bid.find(".star-td").addClass("starred");
+ }
+ }
+ });
+});
+
+$(document).on('show', '.bid-details .collapse', function() {
+ var bid, bid_id;
+ bid = $(this).closest(".bid");
+ bid_id = bid.data('bid-id');
+ $(this).find(".dsbs-certifications").trigger('load-dsbs');
+ bid.removeClass('unread');
+ return Rfpez.view_notification_payload('bid', bid_id, "read");
+});
+
+$(document).on("click", ".undismiss-button", function() {
+ var bid, bid_id, data_el, el, project_id;
+ el = $(this);
+ bid = el.closest(".bid");
+ data_el = el.closest("[data-bid-id]");
+ project_id = data_el.data('project-id');
+ bid_id = data_el.data('bid-id');
+ return $.ajax({
+ url: "/projects/" + project_id + "/bids/" + bid_id + "/dismiss",
+ type: "GET",
+ success: function(data) {
+ var new_bid;
+ if (data.status === "success") {
+ if (el.data('move-to-table')) {
+ Rfpez.move_bid_selection("down");
+ new_bid = $(data.html);
+ bid.remove();
+ return $(".bids-table.open-bids > thead").after(new_bid);
+ } else {
+ return window.location.reload();
+ }
+ }
+ }
+ });
+});
+
+$(document).on("click", ".show-dismiss-modal", function() {
+ var bid, bid_id, data_el, el, modal, project_id, vendor_company_name;
+ el = $(this);
+ bid = el.closest(".bid");
+ data_el = el.closest("[data-bid-id]");
+ project_id = data_el.data('project-id');
+ bid_id = data_el.data('bid-id');
+ vendor_company_name = data_el.data('vendor-company-name');
+ modal = $("#dismiss-modal");
+ modal.find(".company-name").text(vendor_company_name);
+ modal.find("textarea").val("");
+ modal.find(".dismiss-btn").button('reset');
+ modal.modal('show');
+ modal.off(".rfpez-dismiss");
+ return modal.on("submit.rfpez-dismiss", "form", function(e) {
+ e.preventDefault();
+ $(this).find(".dismiss-btn").button('loading');
+ return $.ajax({
+ url: "/projects/" + project_id + "/bids/" + bid_id + "/dismiss",
+ data: {
+ reason: modal.find("select[name=reason]").val(),
+ reason_other: modal.find("input[name=reason_other]").val(),
+ explanation: modal.find("textarea[name=explanation]").val()
+ },
+ type: "GET",
+ dataType: "json",
+ success: function(data) {
+ var new_bid;
+ if (data.status === "already dismissed" || "success") {
+ modal.modal('hide');
+ if (el.data('move-to-table')) {
+ Rfpez.move_bid_selection("down");
+ bid.remove();
+ new_bid = $(data.html);
+ return $(".bids-table.dismissed-bids > thead").after(new_bid);
+ } else {
+ return window.location.reload();
+ }
+ }
+ }
+ });
+ });
+});
+
+$(document).on("click", ".show-award-modal", function() {
+ var bid, bid_id, data_el, el, modal, project_id, vendor_company_name, vendor_email;
+ el = $(this);
+ bid = el.closest(".bid");
+ data_el = el.closest("[data-bid-id]");
+ project_id = data_el.data('project-id');
+ bid_id = data_el.data('bid-id');
+ vendor_company_name = data_el.data('vendor-company-name');
+ vendor_email = data_el.data('vendor-email');
+ modal = $("#award-modal");
+ modal.find(".company-name").text(vendor_company_name);
+ modal.find(".vendor-email").html("<a href=\"mailto:" + vendor_email + "\">" + vendor_email + "</a>");
+ modal.find(".award-btn").button('reset');
+ modal.modal('show');
+ modal.off(".rfpez-award");
+ return modal.on("submit.rfpez-award", "form", function(e) {
+ e.preventDefault();
+ $(this).find(".award-btn").button('loading');
+ return $.ajax({
+ url: "/projects/" + project_id + "/bids/" + bid_id + "/award",
+ data: {
+ awarded_message: modal.find("textarea[name=awarded_message]").val()
+ },
+ type: "GET",
+ dataType: "json",
+ success: function(data) {
+ if (data.status === "success") {
+ modal.modal('hide');
+ return window.location.reload();
+ }
+ }
+ });
+ });
+});
+
+$(document).on("click", ".manual-awarded-message-checkbox", function() {
+ var awarded_message, el, modal;
+ el = $(this);
+ modal = $("#award-modal");
+ awarded_message = modal.find(".awarded-message");
+ if (el.is(":checked")) {
+ return awarded_message.data('original-val', awarded_message.val()).val("").attr('disabled', true);
+ } else {
+ return awarded_message.val(awarded_message.data('original-val')).removeAttr('disabled');
+ }
+});
+
+on_mouseover_select = true;
+
+mouseover_select_timeout = false;
+
+keep_bid_in_view = function(bid, scrollTo) {
+ var bottom, current_bottom, current_top, top;
+ on_mouseover_select = false;
+ clearTimeout(mouseover_select_timeout);
+ if (scrollTo === "bid") {
+ bottom = bid.offset().top + bid.height();
+ current_bottom = $(window).scrollTop() + $(window).height();
+ top = bid.offset().top;
+ current_top = $(window).scrollTop();
+ if (current_bottom < bottom) {
+ $('html, body').scrollTop(bottom - $(window).height());
+ }
+ if (current_top > top) {
+ $('html, body').scrollTop(bid.offset().top);
+ }
+ } else if (scrollTo === "top") {
+ $('html, body').scrollTop(0);
+ }
+ return mouseover_select_timeout = setTimeout(function() {
+ return on_mouseover_select = true;
+ }, 200);
+};
+
+Rfpez.select_bid = function(bid, scrollTo) {
+ $(".bid").removeClass('selected');
+ bid.addClass('selected');
+ if (scrollTo) {
+ return keep_bid_in_view(bid, scrollTo);
+ }
+};
+
+Rfpez.move_bid_selection = function(direction) {
+ var all_bids, new_index, new_selection, selected_bid, selected_index;
+ selected_bid = $(".bid.selected:eq(0)");
+ if (!selected_bid) {
+ return;
+ }
+ all_bids = $(".bid");
+ selected_index = all_bids.index(selected_bid);
+ if (direction === "up") {
+ if (selected_index === 0) {
+ return Rfpez.select_bid(selected_bid, "top");
+ }
+ new_index = selected_index - 1;
+ } else {
+ new_index = selected_index + 1;
+ }
+ new_selection = $(".bid:eq(" + new_index + ")");
+ if (new_selection.length > 0) {
+ return Rfpez.select_bid(new_selection, "bid");
+ }
+};
+
+star_selection = function() {
+ var selected_bid;
+ selected_bid = $(".bid.selected:eq(0)");
+ return selected_bid.find(".star-td .btn:visible").click();
+};
+
+open_selection = function() {
+ var selected_bid;
+ selected_bid = $(".bid.selected:eq(0)");
+ return selected_bid.find("a[data-toggle=collapse]").click();
+};
+
+dismiss_selection = function() {
+ var selected_bid;
+ selected_bid = $(".bid.selected:eq(0)");
+ return selected_bid.find(".show-dismiss-modal, .undismiss-button").filter(":visible").click();
+};
+
+toggle_unread_selection = function() {
+ var selected_bid;
+ selected_bid = $(".bid.selected:eq(0)");
+ if (selected_bid.find(".mark-as-read").is(":visible")) {
+ return selected_bid.find(".mark-as-read").click();
+ } else {
+ return selected_bid.find(".mark-as-unread").click();
+ }
+};
+
+$(document).bind('keydown', 'k', function() {
+ return Rfpez.move_bid_selection("up");
+});
+
+$(document).bind('keydown', 'j', function() {
+ return Rfpez.move_bid_selection("down");
+});
+
+$(document).bind('keydown', 's', star_selection);
+
+$(document).bind('keydown', 'return', open_selection);
+
+$(document).bind('keydown', 'o', open_selection);
+
+$(document).bind('keydown', 'd', dismiss_selection);
+
+$(document).bind('keydown', 'u', toggle_unread_selection);
+
+$(document).on("mouseover.selectbidmouseover", ".bid", function() {
+ if (Rfpez.current_page("bid-review") && on_mouseover_select) {
+ return Rfpez.select_bid($(this), false);
+ }
+});
View
121 assets/js/collaborators-backbone.js
@@ -0,0 +1,121 @@
+var App, AppView, Collaborator, CollaboratorList, CollaboratorView, Collaborators;
+
+Collaborator = Backbone.Model.extend({
+ validate: function(attrs) {
+ var errors;
+ errors = [];
+ if (!attrs.User.email) {
+ return true;
+ } else if (!attrs.User.email.match(/.gov$/i)) {
+ errors.push("Sorry, .gov addresses only");
+ } else if (!attrs.id && Collaborators.existing_emails().indexOf(attrs.User.email.toLowerCase()) !== -1) {
+ errors.push("That collaborator already exists.");
+ }
+ if (errors.length > 0) {
+ App.trigger('errorAdding', errors);
+ return errors;
+ }
+ },
+ defaults: function() {
+ return {
+ owner: false
+ };
+ },
+ clear: function() {
+ return this.destroy();
+ }
+});
+
+CollaboratorList = Backbone.Collection.extend({
+ existing_emails: function() {
+ return this.map(function(c) {
+ return c.attributes.User.email.toLowerCase();
+ });
+ },
+ model: Collaborator
+});
+
+CollaboratorView = Backbone.View.extend({
+ tagName: "tr",
+ template: _.template("<td class=\"email\"><%= User.email %></td>\n<td>\n <% if (pivot.owner === \"1\") { %>\n <i class=\"icon-star\"></i>\n <% } %>\n</td>\n<td>\n <span class=\"not-user-<%= User.id %> only-user only-user-<%= owner_id %>\">\n <% if (pivot.owner !== \"1\") { %>\n <button class=\"btn btn-danger\">Remove</button>\n <% } else { %>\n Can't remove the owner.\n <% } %>\n </span>\n <span class=\"only-user only-user-<%= User.id %>\">\n That's you!\n </span>\n</td>"),
+ events: {
+ "click .btn.btn-danger": "clear"
+ },
+ initialize: function() {
+ this.model.bind("change", this.render, this);
+ return this.model.bind("destroy", this.remove, this);
+ },
+ render: function() {
+ this.$el.html(this.template(_.extend(this.model.toJSON(), {
+ owner_id: App.options.owner_id
+ })));
+ return this;
+ },
+ clear: function() {
+ return this.model.clear();
+ }
+});
+
+AppView = Backbone.View.extend({
+ initialize: function() {
+ Collaborators.bind('add', this.addOne, this);
+ Collaborators.bind('reset', this.reset, this);
+ Collaborators.bind('all', this.render, this);
+ this.bind('errorAdding', this.showError);
+ return $("#add-collaborator-form").submit(this.addNew);
+ },
+ addNew: function(e) {
+ var email;
+ e.preventDefault();
+ email = $("#add-collaborator-form input[name=email]").val();
+ $("#add-collaborator-form input[name=email]").val('');
+ return Collaborators.create({
+ User: {
+ email: email
+ },
+ pivot: {
+ owner: 0
+ }
+ }, {
+ error: function(obj, err) {
+ return obj.clear();
+ }
+ });
+ },
+ showError: function(errors) {
+ return $("#add-collaborator-form button").flash_button_message("warning", errors[0]);
+ },
+ reset: function() {
+ $("#collaborators-tbody").html('');
+ return this.addAll();
+ },
+ render: function() {},
+ addOne: function(collaborator) {
+ var html, view;
+ view = new CollaboratorView({
+ model: collaborator
+ });
+ html = view.render().el;
+ return $("#collaborators-tbody").append(html);
+ },
+ addAll: function() {
+ return Collaborators.each(this.addOne);
+ }
+});
+
+App = false;
+
+Collaborators = false;
+
+Rfpez.Backbone.Collaborators = function(project_id, owner_id, initialModels) {
+ var initialCollection;
+ Collaborators = new CollaboratorList;
+ initialCollection = Collaborators;
+ App = new AppView({
+ collection: initialCollection,
+ owner_id: owner_id
+ });
+ initialCollection.reset(initialModels);
+ initialCollection.url = "/projects/" + project_id + "/collaborators";
+ return App;
+};
View
30 assets/js/collaborators.js
@@ -0,0 +1,30 @@
+
+$(document).on("ready pjax:success", function() {
+ var typeahead_searching;
+ typeahead_searching = false;
+ return $("#add-collaborator-form input[name=email]").typeahead({
+ minLength: 3,
+ source: function(query, process) {
+ clearTimeout(typeahead_searching);
+ return typeahead_searching = setTimeout(function() {
+ var existing_collaborators;
+ existing_collaborators = [];
+ $(".collaborators-table td.email").each(function() {
+ return existing_collaborators.push($(this).text());
+ });
+ return $.ajax({
+ url: "/officers/typeahead",
+ data: {
+ query: query
+ },
+ success: function(data) {
+ data = $.grep(data, function(value) {
+ return existing_collaborators.indexOf(value) === -1;
+ });
+ return process(data);
+ }
+ });
+ }, 200);
+ }
+ });
+});
View
105 assets/js/comments-backbone.js
@@ -0,0 +1,105 @@
+var App, AppView, Comment, CommentList, CommentView, Comments;
+
+Comment = Backbone.Model.extend({
+ validate: function(attrs) {
+ if (!attrs.body) {
+ return true;
+ }
+ },
+ defaults: function() {
+ return {
+ owner: false
+ };
+ },
+ clear: function() {
+ return this.destroy();
+ }
+});
+
+CommentList = Backbone.Collection.extend({
+ model: Comment
+});
+
+CommentView = Backbone.View.extend({
+ tagName: "div",
+ className: "well comment",
+ template: _.template("<div class=\"body\">\n <span class=\"author\">\n <%= officer.name %>\n </span>\n <%= body %>\n</div>\n<span class=\"timestamp\">\n <span class=\"posted-at\">Posted <span class=\"timeago\" title=\"<%= formatted_created_at %>\"></span></span>\n</span>\n<a class=\"delete-comment only-user only-user-<%= officer.user_id %>\">Delete</a>"),
+ events: {
+ "click a.delete-comment": "clear"
+ },
+ initialize: function() {
+ this.model.bind("change", this.render, this);
+ return this.model.bind("destroy", this.remove, this);
+ },
+ render: function() {
+ this.$el.html(this.template(this.model.toJSON()));
+ this.$el.find(".timeago").timeago();
+ return this;
+ },
+ clear: function() {
+ return this.model.clear();
+ }
+});
+
+AppView = Backbone.View.extend({
+ initialize: function() {
+ Comments.bind('add', this.addOne, this);
+ Comments.bind('reset', this.reset, this);
+ Comments.bind('all', this.render, this);
+ this.bind('errorAdding', this.showError);
+ return $("#add-comment-form").submit(this.addNew);
+ },
+ addNew: function(e) {
+ var dateString;
+ e.preventDefault();
+ dateString = new Date().toISOString();
+ Comments.create({
+ officer: {
+ name: $("#add-comment-form").data('officer-name'),
+ user_id: $("#add-comment-form").data('officer-user-id')
+ },
+ body: $("#add-comment-form textarea").val(),
+ formatted_created_at: dateString
+ }, {
+ error: function(obj, err) {
+ return obj.clear();
+ }
+ });
+ return $("#add-comment-form").resetForm();
+ },
+ showError: function(errors) {
+ return alert(errors[0]);
+ },
+ reset: function() {
+ $(".comments-list").html('');
+ return this.addAll();
+ },
+ render: function() {},
+ addOne: function(comment) {
+ var html, view;
+ view = new CommentView({
+ model: comment
+ });
+ html = view.render().el;
+ return $(".comments-list").append(html);
+ },
+ addAll: function() {
+ return Comments.each(this.addOne);
+ }
+});
+
+App = false;
+
+Comments = false;
+
+Rfpez.Backbone.Comments = function(project_id, initialModels) {
+ var initialCollection;
+ Comments = new CommentList;
+ initialCollection = Comments;
+ App = new AppView({
+ collection: initialCollection
+ });
+ initialCollection.reset(initialModels);
+ initialCollection.url = "/projects/" + project_id + "/comments";
+ return App;
+};
View
34 assets/js/dsbs-lookup.js
@@ -0,0 +1,34 @@
+
+$(document).on("ready pjax:success", function() {
+ return $("[data-dsbs-user-id]").each(function() {
+ var el, user_id;
+ el = $(this);
+ user_id = el.data('dsbs-user-id');
+ el.on('load-dsbs', function() {
+ return $.ajax({
+ url: "http://rfpez-apis.presidentialinnovationfellows.org/bizs?user_id=" + user_id,
+ dataType: "json",
+ success: function(data) {
+ var key, result, _results;
+ result = data.results[0];
+ if (!result) {
+ el.removeClass('loading');
+ return el.addClass('no-certs');
+ } else {
+ if (result.user_id === user_id) {
+ el.removeClass('loading');
+ _results = [];
+ for (key in result) {
+ _results.push(el.find("[data-key=" + key + "]").text(result[key]));
+ }
+ return _results;
+ }
+ }
+ }
+ });
+ });
+ if (el.data('defer') === false) {
+ return el.trigger('load-dsbs');
+ }
+ });
+});
View
13 assets/js/filter-projects.js
@@ -0,0 +1,13 @@
+
+$(document).on("change keyup", "#filter-projects-input", function() {
+ var all_contracts, query;
+ query = $.trim($(this).val());
+ all_contracts = $(".projects-table .project");
+ return $(all_contracts).each(function() {
+ if (!query || $(this).text().match(new RegExp(query, 'i'))) {
+ return $(this).removeClass('hide');
+ } else {
+ return $(this).addClass('hide');
+ }
+ });
+});
View
23 assets/js/flash-button.js
@@ -0,0 +1,23 @@
+var $;
+
+$ = jQuery;
+
+$.fn.extend({
+ flash_button_message: function(button_class, message, timeout) {
+ timeout || (timeout = 1000);
+ return this.each(function() {
+ var button, original_classes, original_text;
+ button = $(this);
+ original_text = button.text();
+ original_classes = button.attr('class');
+ button.removeClass('btn-primary btn-info btn-success btn-warning btn-danger btn-inverse btn-link');
+ button.addClass("btn-" + button_class);
+ button.text(message);
+ return setTimeout(function() {
+ button.attr('class', original_classes);
+ button.removeClass('disabled');
+ return button.text(original_text);
+ }, timeout);
+ });
+ }
+});
View
47 assets/js/infinite-vendor-scroll.js
@@ -0,0 +1,47 @@
+var check_for_scroll_position, finished_loading_vendors, load_more_vendors, loading_more_vendors;
+
+loading_more_vendors = false;
+
+finished_loading_vendors = false;
+
+check_for_scroll_position = function() {
+ var bottom_of_vendors, current_scroll_position;
+ bottom_of_vendors = $('.vendors').offset().top + $('.vendors').height();
+ current_scroll_position = $(window).scrollTop() + $(window).height();
+ if (current_scroll_position >= (bottom_of_vendors - 500)) {
+ return load_more_vendors();
+ }
+};
+
+load_more_vendors = function() {
+ var next_page, vendors_div, vendors_wrapper;
+ if (loading_more_vendors === true || finished_loading_vendors === true) {
+ return;
+ }
+ loading_more_vendors = true;
+ vendors_div = $('.vendors');
+ vendors_wrapper = $('.vendors-wrapper');
+ vendors_wrapper.addClass('loading');
+ next_page = (vendors_wrapper.data('current-page') || 1) + 1;
+ return $.ajax({
+ url: "/vendors?page=" + next_page,
+ success: function(data) {
+ var new_vendors;
+ new_vendors = $(data).find(".vendors .vendor");
+ vendors_wrapper.removeClass('loading');
+ loading_more_vendors = false;
+ vendors_wrapper.data('current-page', next_page);
+ vendors_div.append(new_vendors);
+ if (new_vendors.length <= 9) {
+ finished_loading_vendors = true;
+ return vendors_wrapper.addClass('finished-loading');
+ }
+ }
+ });
+};
+
+$(document).on('ready scroll', function() {
+ if (Rfpez.current_page("vendors-index")) {
+ return check_for_scroll_position();
+ }
+});
View
59 assets/js/main.js
@@ -0,0 +1,59 @@
+
+window.Rfpez || (window.Rfpez = {
+ Backbone: {}
+});
+
+Rfpez.current_page = function(str) {
+ if (str === Rfpez.current_page_string) {
+ return true;
+ } else {
+ return false;
+ }
+};
+
+$(document).on('shown', '#signinModal', function() {
+ return $("#signinModal #email").focus();
+});
+
+$(document).on("click", "a[data-confirm]", function(e) {
+ var el;
+ e.preventDefault();
+ el = $(this);
+ if (confirm(el.data('confirm'))) {
+ return window.location = el.attr('href');
+ }
+});
+
+$(document).on("submit", "#new-contract-form", function(e) {
+ if (!$(this).find('input[name=solnbr]').val()) {
+ return e.preventDefault();
+ }
+ return $(this).find("button[type=submit]").button('loading');
+});
+
+$(document).on("click", "[data-select-text-on-focus]", function(e) {
+ return $(this).select();
+});
+
+$(document).on("click", "a[data-pjax]", function(e) {
+ return $.pjax.click(e, "#pjax-container");
+});
+
+$(document).on("mouseenter", ".helper-tooltip", function(e) {
+ $(this).tooltip();
+ return $(this).tooltip('show');
+});
+
+$(document).on("mouseleave", ".helper-tooltip", function(e) {
+ return $(this).tooltip('hide');
+});
+
+$(document).on("ready pjax:success", function() {
+ $("[data-onload-focus]:eq(0)").focus();
+ $("span.timeago").timeago();
+ if ($("body").hasClass('officer')) {
+ $('.datepicker-wrapper').datepicker();
+ $('.wysihtml5').wysihtml5();
+ }
+ return Rfpez.current_page_string = $("#current-page").val();
+});
View
34 assets/js/new-bid.js
@@ -0,0 +1,34 @@
+var update_total_price;
+
+update_total_price = function() {
+ var total;
+ total = 0;
+ $(".deliverable-price").each(function() {
+ var price;
+ if (price = parseFloat($(this).val())) {
+ return total += price;
+ }
+ });
+ return $("#total-price").html("$" + total);
+};
+
+$(document).on("click", "#add-deliverable-button", function() {
+ return $(".deliverables-row:eq(0)").clone().appendTo(".prices-table tbody").find("input").val("");
+});
+
+$(document).on("click", ".remove-deliverable", function() {
+ if ($(".deliverables-row").length === 1) {
+ $(this).closest('.deliverables-row').find(':input').val('');
+ } else {
+ $(this).closest(".deliverables-row").remove();
+ }
+ return update_total_price();
+});
+
+$(document).on("input", ".deliverable-price", update_total_price);
+
+$(document).on("ready pjax:success", function() {
+ if ($("#current-page").val() === "new-bid") {
+ return update_total_price();
+ }
+});
View
102 assets/js/notifications.js
@@ -0,0 +1,102 @@
+var notifications_loaded, render_notification, reset_notification_dropdown;
+
+Rfpez.unread_notification_count = function() {
+ return parseInt($(".notification-nav-item .unread-notification-badge").text());
+};
+
+Rfpez.update_notification_badge = function(count) {
+ var badge, nav_item;
+ nav_item = $(".notification-nav-item");
+ badge = nav_item.find(".unread-notification-badge");
+ if (count > 0) {
+ badge.text(count);
+ return badge.removeClass('hide');
+ } else {
+ badge.text(0);
+ return badge.addClass('hide');
+ }
+};
+
+Rfpez.view_notification_payload = function(key, val, mark_as) {
+ mark_as || (mark_as = "read");
+ if (mark_as === "read" && Rfpez.unread_notification_count() === 0) {
+ return;
+ }
+ return $.ajax({
+ url: "/account/viewnotifications/" + key + "/" + val,
+ type: "PUT",
+ data: {
+ action: mark_as
+ },
+ success: function(data) {
+ Rfpez.update_notification_badge(data.unread_count);
+ return reset_notification_dropdown();
+ }
+ });
+};
+
+render_notification = function(notification) {
+ return "<li class=\"notification " + (notification.object.read === '0' ? 'unread' : 'read') + "\">\n <a href=\"" + notification.parsed.link + "\" data-pjax>\n <span class=\"line1\">" + notification.parsed.subject + "</span>\n <span class=\"timeago\" title=\"" + notification.parsed.timestamp + "\"></span>\n </a>\n</li>";
+};
+
+notifications_loaded = false;
+
+reset_notification_dropdown = function() {
+ notifications_loaded = false;
+ $("#notifications-dropdown").addClass('loading');
+ return $("#notifications-dropdown").html('');
+};
+
+$(document).on("click", ".notification-item .mark-as-read, .notification-item .mark-as-unread", function() {
+ var action, data_el, el, notification_id, notification_item;
+ el = $(this);
+ notification_item = el.closest(".notification-item");
+ data_el = el.closest("[data-notification-id]");
+ notification_id = data_el.data('notification-id');
+ action = el.hasClass('mark-as-read') ? 1 : 0;
+ return $.ajax({
+ url: "/notifications/" + notification_id + "/markasread",
+ type: "PUT",
+ data: {
+ action: action
+ },
+ success: function(data) {
+ var new_notification_item;
+ if (data.status === "success") {
+ new_notification_item = $(data.html);
+ notification_item.replaceWith(new_notification_item);
+ return Rfpez.update_notification_badge(data.unread_count);
+ }
+ }
+ });
+});
+
+$(document).on("ready pjax:success pjax:popstate", function() {
+ notifications_loaded = false;
+ return $("#notifications-dropdown-trigger").on("click", function() {
+ if (notifications_loaded) {
+ return;
+ }
+ return $.ajax({
+ url: "/notifications/json",
+ dataType: "json",
+ success: function(data) {
+ var str;
+ if (data.status === "success") {
+ if (data.count < 1) {
+ return $("#notifications-dropdown").removeClass('loading').addClass('none');
+ }
+ str = "";
+ $(data.results).each(function() {
+ return str += render_notification(this);
+ });
+ str += "<li class=\"view-all\"><a href=\"/notifications\" data-pjax>view all " + data.count + " notifications</a></li>";
+ $("#notifications-dropdown").removeClass("loading");
+ $("#notifications-dropdown").html(str);
+ $("#notifications-dropdown span.timeago").timeago();
+ return notifications_loaded = true;
+ }
+ }
+ });
+ });
+});
View
76 assets/js/question-and-answer.js
@@ -0,0 +1,76 @@
+
+$(document).on("submit", "#answer-question-form", function(e) {
+ var answer_text, el, question, question_id;
+ e.preventDefault();
+ el = $(this);
+ answer_text = el.find("textarea[name=answer]").val();
+ if (!answer_text) {
+ return;
+ }
+ el.find("button").button('loading');
+ question = el.closest(".question-wrapper");
+ question_id = el.closest("[data-question-id]").data('question-id');
+ return el.ajaxSubmit({
+ url: "/questions/" + question_id,
+ success: function(data) {
+ var new_question;
+ if (data.status === "success") {
+ el.hide();
+ el.find("button").button('reset');
+ el.prependTo('body');
+ question.find(".answer-question").remove();
+ new_question = $(data.html);
+ new_question.find(".answer").hide();
+ question.replaceWith(new_question);
+ return new_question.find(".answer").fadeIn(300);
+ } else {
+ return el.find('button').button('reset').flash_button_message("danger", "Error occurred");
+ }
+ }
+ });
+});
+
+$(document).on("click", ".answer-question-toggle", function() {
+ var el, form, question;
+ el = $(this);
+ question = $(this).closest(".question-wrapper");
+ form = $("#answer-question-form");
+ form.find("input[name=id]").val(question.data('id'));
+ form.find("textarea[name=answer]").val('');
+ form.appendTo(question);
+ return form.show();
+});
+
+$(document).on("submit", "#ask-question-form", function(e) {
+ var button, el, question_text;
+ e.preventDefault();
+ el = $(this);
+ question_text = el.find("textarea[name=question]").val();
+ if (!question_text) {
+ return;
+ }
+ button = el.find("button");
+ button.button('loading');
+ return $.ajax({
+ url: "/questions",
+ data: {
+ project_id: el.find("input[name=project_id]").val(),
+ question: question_text
+ },
+ type: "POST",
+ success: function(data) {
+ var new_question;
+ button.button('reset');
+ el.find("textarea[name=question]").val('');
+ if (data.status === "success") {
+ $("p.no-questions-asked").hide();
+ new_question = $(data.html);
+ new_question.hide();
+ $(".questions").append(new_question);
+ return new_question.fadeIn(300);
+ } else {
+ return alert('error!');
+ }
+ }
+ });
+});
View
30 assets/js/save-bid-draft.js
@@ -0,0 +1,30 @@
+
+$(document).on("ready pjax:success", function() {
+ var draft_saved, form_update_handler, save_draft, save_draft_button;
+ if (!Rfpez.current_page("new-bid")) {
+ return;
+ }
+ draft_saved = true;
+ save_draft_button = $("#save-draft-button");
+ save_draft_button.button('loading');
+ form_update_handler = function() {
+ draft_saved = false;
+ return save_draft_button.button('reset');
+ };
+ save_draft = function() {
+ var form;
+ if (draft_saved === true) {
+ return;
+ }
+ form = $(".new-bid-form");
+ form.find("input[name=submit_now]").val('false');
+ form.ajaxSubmit();
+ form.find("input[name=submit_now]").val('true');
+ draft_saved = true;
+ return save_draft_button.button('loading');
+ };
+ $("#save-draft-button").on("click", save_draft);
+ $(".new-bid-form :input").on("input", form_update_handler);
+ $("#add-deliverable-button, .remove-deliverable").on("click", form_update_handler);
+ return window.setInterval(save_draft, 5000);
+});
View
347 assets/js/sow-composer.js
@@ -0,0 +1,347 @@
+var add_empty_class_to_inputs, apply_section_cover, available_sections_filter_timeout, hide_already_selected_sections, remove_section_cover, save_sort_order, search_available_sections, section_category_dropdown_changed, update_section_category_dropdown_from_input;
+
+hide_already_selected_sections = function() {
+ var selected_section_ids, showing_at_least_one_section;
+ selected_section_ids = [];
+ showing_at_least_one_section = false;
+ $(".sections-for-editing .section").each(function() {
+ return selected_section_ids.push($(this).data('section-id'));
+ });
+ $(".available-sections-table .section").each(function() {
+ var el, section_id;
+ el = $(this);
+ section_id = el.data('section-id');
+ if (selected_section_ids.indexOf(section_id) !== -1) {
+ return el.hide();
+ } else {
+ showing_at_least_one_section = true;
+ return el.show();
+ }
+ });
+ if (!showing_at_least_one_section) {
+ return $(".available-sections-table .no-sections").show();
+ } else {
+ return $(".available-sections-table .no-sections").hide();
+ }
+};
+
+apply_section_cover = function() {
+ var cover, sections_wrapper;
+ cover = $("<div class='sections-for-editing-cover'>Saving order...</div>");
+ sections_wrapper = $(".sections-for-editing-wrapper");
+ cover.css({
+ width: sections_wrapper.width(),
+ height: sections_wrapper.height()
+ });
+ return cover.appendTo(sections_wrapper);
+};
+
+remove_section_cover = function() {
+ return $(".sections-for-editing-cover").remove();
+};
+
+save_sort_order = function() {
+ var project_id, sections;
+ apply_section_cover();
+ project_id = $(".sections-for-editing-wrapper").data('project-id');
+ sections = [];
+ $(".sections-for-editing-wrapper .section").each(function() {
+ return sections.push($(this).data('section-id'));
+ });
+ return $.ajax({
+ url: "/projects/" + project_id + "/sections/reorder",
+ type: "POST",
+ data: {
+ sections: sections
+ },
+ success: function(data) {
+ return remove_section_cover();
+ }
+ });
+};
+
+update_section_category_dropdown_from_input = function() {
+ var option, val;
+ val = $("#section-category-input").val();
+ option = $("#section-category-select option[value='" + val + "']");
+ if (option.length > 0) {
+ option.attr(