From 0f43e98ef8c2da8908b1107f75b67cda2572c2c4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sun, 9 Oct 2011 00:34:49 +0300 Subject: gitignore --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..c83fcb6871d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.bundle +.rbx/ +db/*.sqlite3 +log/*.log +tmp/ +.sass-cache/ +coverage/* +*.swp +public/uploads/ -- cgit v1.2.1 From d3784687943e0bd699d73d82a6bc6cac39689473 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 13 Oct 2011 04:00:00 +0300 Subject: v1.0 --- .rails_footnotes | 3 + .rspec | 1 + .rvmrc | 1 + CHANGELOG | 28 + Gemfile | 50 ++ Gemfile.lock | 277 +++++++ LICENSE | 19 + README.rdoc | 85 +++ Rakefile | 7 + VERSION | 1 + app/assets/images/.directory | 4 + app/assets/images/ajax-loader.gif | Bin 0 -> 6820 bytes app/assets/images/blueprint_add.png | Bin 0 -> 4544 bytes app/assets/images/blueprint_delete.png | Bin 0 -> 4506 bytes app/assets/images/blueprint_info.png | Bin 0 -> 4512 bytes app/assets/images/blueprint_notice.png | Bin 0 -> 4531 bytes app/assets/images/dir.png | Bin 0 -> 485 bytes app/assets/images/favicon.ico | Bin 0 -> 1150 bytes app/assets/images/favicon.png | Bin 0 -> 338 bytes app/assets/images/git.png | Bin 0 -> 21559 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../jquery_ui/images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../jquery_ui/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 5355 bytes .../jquery_ui/images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../jquery_ui/images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../jquery_ui/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes app/assets/images/list_view_icon.jpg | Bin 0 -> 357 bytes app/assets/images/no_avatar.png | Bin 0 -> 1335 bytes app/assets/images/rails.png | Bin 0 -> 6646 bytes app/assets/images/txt.png | Bin 0 -> 290 bytes app/assets/javascripts/application.js | 18 + app/assets/javascripts/commits.js | 9 + app/assets/javascripts/dashboard.js.coffee | 3 + app/assets/javascripts/issues.js.coffee | 3 + .../javascripts/jquery-ui-1.8.16.custom.min.js | 791 +++++++++++++++++++ app/assets/javascripts/jquery.cookie.js | 41 + app/assets/javascripts/jquery.ui.selectmenu.js | 845 +++++++++++++++++++++ app/assets/javascripts/profile.js.coffee | 3 + app/assets/javascripts/projects.js | 41 + app/assets/stylesheets/application.css | 7 + app/assets/stylesheets/dashboard.css.scss | 3 + app/assets/stylesheets/highlight.css.scss | 135 ++++ app/assets/stylesheets/issues.css.scss | 3 + app/assets/stylesheets/jquery.ui.selectmenu.css | 33 + app/assets/stylesheets/profile.css.scss | 3 + app/assets/stylesheets/projects.css.scss | 541 +++++++++++++ app/controllers/admin/mailer_controller.rb | 44 ++ app/controllers/admin/projects_controller.rb | 74 ++ app/controllers/admin/team_members_controller.rb | 75 ++ app/controllers/admin/users_controller.rb | 84 ++ app/controllers/application_controller.rb | 44 ++ app/controllers/commits_controller.rb | 44 ++ app/controllers/dashboard_controller.rb | 5 + app/controllers/errors_controller.rb | 5 + app/controllers/issues_controller.rb | 72 ++ app/controllers/keys_controller.rb | 38 + app/controllers/notes_controller.rb | 49 ++ app/controllers/profile_controller.rb | 21 + app/controllers/projects_controller.rb | 152 ++++ app/controllers/team_members_controller.rb | 66 ++ app/helpers/admin/projects_helper.rb | 2 + app/helpers/admin/users_helper.rb | 2 + app/helpers/application_helper.rb | 77 ++ app/helpers/commits_helper.rb | 24 + app/helpers/dashboard_helper.rb | 2 + app/helpers/issues_helper.rb | 2 + app/helpers/keys_helper.rb | 2 + app/helpers/profile_helper.rb | 2 + app/helpers/projects_helper.rb | 6 + app/helpers/team_members_helper.rb | 2 + app/mailers/.gitkeep | 0 app/mailers/notify.rb | 41 + app/models/.gitkeep | 0 app/models/ability.rb | 34 + app/models/issue.rb | 39 + app/models/key.rb | 58 ++ app/models/note.rb | 41 + app/models/project.rb | 164 ++++ app/models/user.rb | 62 ++ app/models/users_project.rb | 35 + app/uploaders/attachment_uploader.rb | 49 ++ app/views/admin/_top_menu.html.haml | 6 + app/views/admin/mailer/preview.html.haml | 29 + app/views/admin/projects/_form.html.haml | 30 + app/views/admin/projects/edit.html.haml | 5 + app/views/admin/projects/index.html.haml | 26 + app/views/admin/projects/new.html.haml | 5 + app/views/admin/projects/show.html.haml | 45 ++ app/views/admin/team_members/_form.html.haml | 34 + app/views/admin/team_members/edit.html.haml | 5 + app/views/admin/team_members/index.html.haml | 30 + app/views/admin/team_members/new.html.haml | 5 + app/views/admin/team_members/show.html.haml | 32 + app/views/admin/users/_form.html.haml | 38 + app/views/admin/users/edit.html.haml | 4 + app/views/admin/users/index.html.haml | 24 + app/views/admin/users/new.html.haml | 6 + app/views/admin/users/show.html.haml | 45 ++ app/views/commits/_commits.html.haml | 22 + app/views/commits/_diff.html.haml | 58 ++ app/views/commits/_index.html.haml | 9 + app/views/commits/index.html.haml | 13 + app/views/commits/index.js.erb | 2 + app/views/commits/show.html.haml | 39 + app/views/commits/show.js.haml | 6 + app/views/dashboard/index.html.haml | 1 + app/views/devise/confirmations/new.html.erb | 12 + .../mailer/confirmation_instructions.html.erb | 5 + .../mailer/reset_password_instructions.html.erb | 8 + .../devise/mailer/unlock_instructions.html.erb | 7 + app/views/devise/passwords/edit.html.erb | 16 + app/views/devise/passwords/new.html.erb | 15 + app/views/devise/registrations/edit.html.erb | 28 + app/views/devise/registrations/new.html.erb | 18 + app/views/devise/sessions/new.html.erb | 20 + app/views/devise/shared/_links.erb | 25 + app/views/devise/unlocks/new.html.erb | 12 + app/views/issues/_form.html.haml | 24 + app/views/issues/_issues.html.haml | 10 + app/views/issues/_show.html.haml | 18 + app/views/issues/create.js.haml | 9 + app/views/issues/edit.js.haml | 12 + app/views/issues/index.html.haml | 24 + app/views/issues/index.js.haml | 2 + app/views/issues/new.js.haml | 12 + app/views/issues/show.html.haml | 44 ++ app/views/issues/update.js.haml | 14 + app/views/keys/_form.html.haml | 16 + app/views/keys/_show.html.haml | 4 + app/views/keys/create.js.haml | 8 + app/views/keys/edit.html.haml | 7 + app/views/keys/index.html.haml | 15 + app/views/keys/new.html.haml | 5 + app/views/keys/new.js.haml | 11 + app/views/layouts/_flash.html.haml | 18 + app/views/layouts/_head_panel.html.erb | 34 + app/views/layouts/application.html.haml | 29 + app/views/layouts/notify.html.haml | 36 + app/views/notes/_form.html.haml | 28 + app/views/notes/_notes.html.haml | 14 + app/views/notes/_show.html.haml | 19 + app/views/notes/create.js.haml | 8 + app/views/notify/new_issue_email.html.haml | 18 + app/views/notify/new_user_email.html.haml | 23 + app/views/notify/note_commit_email.html.haml | 23 + app/views/notify/note_issue_email.html.haml | 25 + app/views/notify/note_wall_email.html.haml | 22 + app/views/profile/_top_menu.html.haml | 5 + app/views/profile/index.html.haml | 1 + app/views/profile/password.html.haml | 20 + app/views/profile/show.html.haml | 8 + app/views/projects/_form.html.haml | 50 ++ app/views/projects/_list.html.haml | 26 + app/views/projects/_projects_top_menu.html.haml | 16 + app/views/projects/_side_panel.html.haml | 14 + app/views/projects/_team.html.haml | 18 + app/views/projects/_tile.html.haml | 16 + app/views/projects/_top_menu.html.haml | 24 + app/views/projects/_tree.html.haml | 60 ++ app/views/projects/_tree_file.html.haml | 21 + app/views/projects/_tree_item.html.haml | 15 + app/views/projects/create.js.haml | 6 + app/views/projects/edit.html.erb | 1 + app/views/projects/empty.html.erb | 49 ++ app/views/projects/index.html.haml | 4 + app/views/projects/new.html.erb | 1 + app/views/projects/show.html.haml | 3 + app/views/projects/team.html.haml | 3 + app/views/projects/tree.html.erb | 5 + app/views/projects/tree.js.haml | 5 + app/views/projects/update.js.haml | 6 + app/views/projects/wall.html.haml | 1 + app/views/team_members/_form.html.haml | 25 + app/views/team_members/_show.html.haml | 18 + app/views/team_members/create.js.haml | 9 + app/views/team_members/new.js.haml | 15 + app/views/team_members/update.js.haml | 6 + config.ru | 4 + config/application.rb | 48 ++ config/boot.rb | 6 + config/database.yml | 25 + config/environment.rb | 7 + config/environments/development.rb | 32 + config/environments/production.rb | 70 ++ config/environments/test.rb | 42 + config/gitosis.yml | 4 + config/initializers/backtrace_silencers.rb | 7 + config/initializers/devise.rb | 211 +++++ config/initializers/inflections.rb | 10 + config/initializers/load_config.rb | 1 + config/initializers/mime_types.rb | 5 + config/initializers/rails_footnotes.rb | 5 + config/initializers/secret_token.rb | 7 + config/initializers/session_store.rb | 8 + config/initializers/wrap_parameters.rb | 14 + config/locales/devise.en.yml | 58 ++ config/locales/en.yml | 10 + config/routes.rb | 47 ++ configure.rb | 5 + db/fixtures/development/001_admin.rb | 11 + db/fixtures/production/001_admin.rb | 10 + db/fixtures/test/001_repo.rb | 11 + db/migrate/20110913200833_devise_create_users.rb | 28 + db/migrate/20110913204141_create_projects.rb | 11 + db/migrate/20110914221600_create_users_projects.rb | 13 + .../20110915205627_add_private_flag_to_project.rb | 5 + db/migrate/20110915213352_create_keys.rb | 9 + db/migrate/20110916123731_add_name_to_user.rb | 5 + db/migrate/20110916162511_add_key_title_to_key.rb | 7 + db/migrate/20110917212932_add_identifier_to_key.rb | 5 + db/migrate/20110921192501_create_issues.rb | 13 + db/migrate/20110922110156_add_code_to_project.rb | 5 + db/migrate/20110923211333_add_status_to_issue.rb | 5 + ...924214549_create_rails_admin_histories_table.rb | 18 + .../20110924215658_add_admin_field_to_user.rb | 5 + db/migrate/20110926082616_remove_admin.rb | 9 + db/migrate/20110927130352_create_notes.rb | 12 + .../20110928140106_add_project_id_for_note.rb | 9 + .../20110928142747_change_noteable_id_for_note.rb | 9 + .../20110928161328_add_attachment_to_note.rb | 5 + ...11005193700_add_allow_repo_creation_for_user.rb | 9 + db/migrate/20111009101738_add_ownerto_project.rb | 5 + .../20111009110913_add_projects_limit_to_user.rb | 5 + ...009111204_remove_allow_create_repo_from_user.rb | 9 + db/pkey.example | 3 + db/schema.rb | 89 +++ db/seeds.rb | 0 doc/README_FOR_APP | 2 + install/prepare.rb | 51 ++ lib/assets/.gitkeep | 0 lib/file_size_validator.rb | 65 ++ lib/gitosis.rb | 73 ++ lib/tasks/.gitkeep | 0 lib/utils.rb | 8 + log/.gitkeep | 0 public/404.html | 25 + public/422.html | 25 + public/500.html | 25 + public/favicon.ico | 0 public/gitosis_error.html | 26 + public/index.html.example | 241 ++++++ public/robots.txt | 5 + script/rails | 6 + spec/factories.rb | 45 ++ spec/factory.rb | 29 + spec/models/issue_spec.rb | 42 + spec/models/key_spec.rb | 32 + spec/models/note_spec.rb | 78 ++ spec/models/project_security_spec.rb | 57 ++ spec/models/project_spec.rb | 127 ++++ spec/models/user_spec.rb | 43 ++ spec/models/users_project_spec.rb | 32 + spec/monkeypatch.rb | 31 + spec/requests/admin/admin_projects_spec.rb | 106 +++ spec/requests/admin/admin_users_spec.rb | 102 +++ spec/requests/admin/security_spec.rb | 27 + spec/requests/commits_notes_spec.rb | 24 + spec/requests/commits_spec.rb | 39 + spec/requests/issues_notes_spec.rb | 27 + spec/requests/issues_spec.rb | 147 ++++ spec/requests/keys_spec.rb | 54 ++ spec/requests/profile_spec.rb | 55 ++ spec/requests/projects_security_spec.rb | 111 +++ spec/requests/projects_spec.rb | 152 ++++ spec/requests/projects_tree_spec.rb | 92 +++ spec/requests/projects_wall_spec.rb | 33 + spec/requests/team_members_spec.rb | 46 ++ spec/requests/top_panel_spec.rb | 34 + spec/requests/user_security_spec.rb | 37 + spec/seed_project.tar.gz | Bin 0 -> 1699843 bytes spec/spec_helper.rb | 56 ++ spec/support/js_patch.rb | 6 + spec/support/login.rb | 29 + spec/support/matchers.rb | 46 ++ spec/support/security.rb | 0 spec/support/shared_examples.rb | 18 + spec/support/valid_commit.rb | 25 + vendor/assets/stylesheets/.gitkeep | 0 vendor/assets/stylesheets/blueprint/ie.css | 36 + .../blueprint/plugins/buttons/icons/cross.png | Bin 0 -> 655 bytes .../blueprint/plugins/buttons/icons/key.png | Bin 0 -> 455 bytes .../blueprint/plugins/buttons/icons/tick.png | Bin 0 -> 537 bytes .../blueprint/plugins/buttons/readme.txt | 32 + .../blueprint/plugins/buttons/screen.css | 97 +++ .../blueprint/plugins/fancy-type/readme.txt | 14 + .../blueprint/plugins/fancy-type/screen.css | 71 ++ .../blueprint/plugins/link-icons/icons/doc.png | Bin 0 -> 777 bytes .../blueprint/plugins/link-icons/icons/email.png | Bin 0 -> 641 bytes .../plugins/link-icons/icons/external.png | Bin 0 -> 46848 bytes .../blueprint/plugins/link-icons/icons/feed.png | Bin 0 -> 691 bytes .../blueprint/plugins/link-icons/icons/im.png | Bin 0 -> 741 bytes .../blueprint/plugins/link-icons/icons/lock.png | Bin 0 -> 749 bytes .../blueprint/plugins/link-icons/icons/pdf.png | Bin 0 -> 591 bytes .../blueprint/plugins/link-icons/icons/visited.png | Bin 0 -> 46990 bytes .../blueprint/plugins/link-icons/icons/xls.png | Bin 0 -> 663 bytes .../blueprint/plugins/link-icons/readme.txt | 18 + .../blueprint/plugins/link-icons/screen.css | 42 + .../stylesheets/blueprint/plugins/rtl/readme.txt | 10 + .../stylesheets/blueprint/plugins/rtl/screen.css | 110 +++ vendor/assets/stylesheets/blueprint/print.css | 29 + vendor/assets/stylesheets/blueprint/screen.css | 265 +++++++ vendor/assets/stylesheets/blueprint/src/forms.css | 82 ++ vendor/assets/stylesheets/blueprint/src/grid.css | 280 +++++++ vendor/assets/stylesheets/blueprint/src/grid.png | Bin 0 -> 195 bytes vendor/assets/stylesheets/blueprint/src/ie.css | 79 ++ vendor/assets/stylesheets/blueprint/src/print.css | 92 +++ vendor/assets/stylesheets/blueprint/src/reset.css | 65 ++ .../stylesheets/blueprint/src/typography.css | 123 +++ .../jquery_ui/jquery-ui-1.8.16.custom.css | 577 ++++++++++++++ vendor/plugins/.gitkeep | 0 317 files changed, 11347 insertions(+) create mode 100644 .rails_footnotes create mode 100644 .rspec create mode 100644 .rvmrc create mode 100644 CHANGELOG create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 LICENSE create mode 100644 README.rdoc create mode 100644 Rakefile create mode 100644 VERSION create mode 100644 app/assets/images/.directory create mode 100644 app/assets/images/ajax-loader.gif create mode 100644 app/assets/images/blueprint_add.png create mode 100644 app/assets/images/blueprint_delete.png create mode 100644 app/assets/images/blueprint_info.png create mode 100644 app/assets/images/blueprint_notice.png create mode 100644 app/assets/images/dir.png create mode 100644 app/assets/images/favicon.ico create mode 100644 app/assets/images/favicon.png create mode 100644 app/assets/images/git.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 app/assets/images/jquery_ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 app/assets/images/jquery_ui/images/ui-icons_222222_256x240.png create mode 100644 app/assets/images/jquery_ui/images/ui-icons_2e83ff_256x240.png create mode 100644 app/assets/images/jquery_ui/images/ui-icons_454545_256x240.png create mode 100644 app/assets/images/jquery_ui/images/ui-icons_888888_256x240.png create mode 100644 app/assets/images/jquery_ui/images/ui-icons_cd0a0a_256x240.png create mode 100644 app/assets/images/list_view_icon.jpg create mode 100644 app/assets/images/no_avatar.png create mode 100644 app/assets/images/rails.png create mode 100644 app/assets/images/txt.png create mode 100644 app/assets/javascripts/application.js create mode 100644 app/assets/javascripts/commits.js create mode 100644 app/assets/javascripts/dashboard.js.coffee create mode 100644 app/assets/javascripts/issues.js.coffee create mode 100644 app/assets/javascripts/jquery-ui-1.8.16.custom.min.js create mode 100644 app/assets/javascripts/jquery.cookie.js create mode 100644 app/assets/javascripts/jquery.ui.selectmenu.js create mode 100644 app/assets/javascripts/profile.js.coffee create mode 100644 app/assets/javascripts/projects.js create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/dashboard.css.scss create mode 100644 app/assets/stylesheets/highlight.css.scss create mode 100644 app/assets/stylesheets/issues.css.scss create mode 100644 app/assets/stylesheets/jquery.ui.selectmenu.css create mode 100644 app/assets/stylesheets/profile.css.scss create mode 100644 app/assets/stylesheets/projects.css.scss create mode 100644 app/controllers/admin/mailer_controller.rb create mode 100644 app/controllers/admin/projects_controller.rb create mode 100644 app/controllers/admin/team_members_controller.rb create mode 100644 app/controllers/admin/users_controller.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/commits_controller.rb create mode 100644 app/controllers/dashboard_controller.rb create mode 100644 app/controllers/errors_controller.rb create mode 100644 app/controllers/issues_controller.rb create mode 100644 app/controllers/keys_controller.rb create mode 100644 app/controllers/notes_controller.rb create mode 100644 app/controllers/profile_controller.rb create mode 100644 app/controllers/projects_controller.rb create mode 100644 app/controllers/team_members_controller.rb create mode 100644 app/helpers/admin/projects_helper.rb create mode 100644 app/helpers/admin/users_helper.rb create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/commits_helper.rb create mode 100644 app/helpers/dashboard_helper.rb create mode 100644 app/helpers/issues_helper.rb create mode 100644 app/helpers/keys_helper.rb create mode 100644 app/helpers/profile_helper.rb create mode 100644 app/helpers/projects_helper.rb create mode 100644 app/helpers/team_members_helper.rb create mode 100644 app/mailers/.gitkeep create mode 100644 app/mailers/notify.rb create mode 100644 app/models/.gitkeep create mode 100644 app/models/ability.rb create mode 100644 app/models/issue.rb create mode 100644 app/models/key.rb create mode 100644 app/models/note.rb create mode 100644 app/models/project.rb create mode 100644 app/models/user.rb create mode 100644 app/models/users_project.rb create mode 100644 app/uploaders/attachment_uploader.rb create mode 100644 app/views/admin/_top_menu.html.haml create mode 100644 app/views/admin/mailer/preview.html.haml create mode 100644 app/views/admin/projects/_form.html.haml create mode 100644 app/views/admin/projects/edit.html.haml create mode 100644 app/views/admin/projects/index.html.haml create mode 100644 app/views/admin/projects/new.html.haml create mode 100644 app/views/admin/projects/show.html.haml create mode 100644 app/views/admin/team_members/_form.html.haml create mode 100644 app/views/admin/team_members/edit.html.haml create mode 100644 app/views/admin/team_members/index.html.haml create mode 100644 app/views/admin/team_members/new.html.haml create mode 100644 app/views/admin/team_members/show.html.haml create mode 100644 app/views/admin/users/_form.html.haml create mode 100644 app/views/admin/users/edit.html.haml create mode 100644 app/views/admin/users/index.html.haml create mode 100644 app/views/admin/users/new.html.haml create mode 100644 app/views/admin/users/show.html.haml create mode 100644 app/views/commits/_commits.html.haml create mode 100644 app/views/commits/_diff.html.haml create mode 100644 app/views/commits/_index.html.haml create mode 100644 app/views/commits/index.html.haml create mode 100644 app/views/commits/index.js.erb create mode 100644 app/views/commits/show.html.haml create mode 100644 app/views/commits/show.js.haml create mode 100644 app/views/dashboard/index.html.haml create mode 100644 app/views/devise/confirmations/new.html.erb create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.erb create mode 100644 app/views/devise/passwords/new.html.erb create mode 100644 app/views/devise/registrations/edit.html.erb create mode 100644 app/views/devise/registrations/new.html.erb create mode 100644 app/views/devise/sessions/new.html.erb create mode 100644 app/views/devise/shared/_links.erb create mode 100644 app/views/devise/unlocks/new.html.erb create mode 100644 app/views/issues/_form.html.haml create mode 100644 app/views/issues/_issues.html.haml create mode 100644 app/views/issues/_show.html.haml create mode 100644 app/views/issues/create.js.haml create mode 100644 app/views/issues/edit.js.haml create mode 100644 app/views/issues/index.html.haml create mode 100644 app/views/issues/index.js.haml create mode 100644 app/views/issues/new.js.haml create mode 100644 app/views/issues/show.html.haml create mode 100644 app/views/issues/update.js.haml create mode 100644 app/views/keys/_form.html.haml create mode 100644 app/views/keys/_show.html.haml create mode 100644 app/views/keys/create.js.haml create mode 100644 app/views/keys/edit.html.haml create mode 100644 app/views/keys/index.html.haml create mode 100644 app/views/keys/new.html.haml create mode 100644 app/views/keys/new.js.haml create mode 100644 app/views/layouts/_flash.html.haml create mode 100644 app/views/layouts/_head_panel.html.erb create mode 100644 app/views/layouts/application.html.haml create mode 100644 app/views/layouts/notify.html.haml create mode 100644 app/views/notes/_form.html.haml create mode 100644 app/views/notes/_notes.html.haml create mode 100644 app/views/notes/_show.html.haml create mode 100644 app/views/notes/create.js.haml create mode 100644 app/views/notify/new_issue_email.html.haml create mode 100644 app/views/notify/new_user_email.html.haml create mode 100644 app/views/notify/note_commit_email.html.haml create mode 100644 app/views/notify/note_issue_email.html.haml create mode 100644 app/views/notify/note_wall_email.html.haml create mode 100644 app/views/profile/_top_menu.html.haml create mode 100644 app/views/profile/index.html.haml create mode 100644 app/views/profile/password.html.haml create mode 100644 app/views/profile/show.html.haml create mode 100644 app/views/projects/_form.html.haml create mode 100644 app/views/projects/_list.html.haml create mode 100644 app/views/projects/_projects_top_menu.html.haml create mode 100644 app/views/projects/_side_panel.html.haml create mode 100644 app/views/projects/_team.html.haml create mode 100644 app/views/projects/_tile.html.haml create mode 100644 app/views/projects/_top_menu.html.haml create mode 100644 app/views/projects/_tree.html.haml create mode 100644 app/views/projects/_tree_file.html.haml create mode 100644 app/views/projects/_tree_item.html.haml create mode 100644 app/views/projects/create.js.haml create mode 100644 app/views/projects/edit.html.erb create mode 100644 app/views/projects/empty.html.erb create mode 100644 app/views/projects/index.html.haml create mode 100644 app/views/projects/new.html.erb create mode 100644 app/views/projects/show.html.haml create mode 100644 app/views/projects/team.html.haml create mode 100644 app/views/projects/tree.html.erb create mode 100644 app/views/projects/tree.js.haml create mode 100644 app/views/projects/update.js.haml create mode 100644 app/views/projects/wall.html.haml create mode 100644 app/views/team_members/_form.html.haml create mode 100644 app/views/team_members/_show.html.haml create mode 100644 app/views/team_members/create.js.haml create mode 100644 app/views/team_members/new.js.haml create mode 100644 app/views/team_members/update.js.haml create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/database.yml create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/gitosis.yml create mode 100644 config/initializers/backtrace_silencers.rb create mode 100644 config/initializers/devise.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/load_config.rb create mode 100644 config/initializers/mime_types.rb create mode 100644 config/initializers/rails_footnotes.rb create mode 100644 config/initializers/secret_token.rb create mode 100644 config/initializers/session_store.rb create mode 100644 config/initializers/wrap_parameters.rb create mode 100644 config/locales/devise.en.yml create mode 100644 config/locales/en.yml create mode 100644 config/routes.rb create mode 100644 configure.rb create mode 100644 db/fixtures/development/001_admin.rb create mode 100644 db/fixtures/production/001_admin.rb create mode 100644 db/fixtures/test/001_repo.rb create mode 100644 db/migrate/20110913200833_devise_create_users.rb create mode 100644 db/migrate/20110913204141_create_projects.rb create mode 100644 db/migrate/20110914221600_create_users_projects.rb create mode 100644 db/migrate/20110915205627_add_private_flag_to_project.rb create mode 100644 db/migrate/20110915213352_create_keys.rb create mode 100644 db/migrate/20110916123731_add_name_to_user.rb create mode 100644 db/migrate/20110916162511_add_key_title_to_key.rb create mode 100644 db/migrate/20110917212932_add_identifier_to_key.rb create mode 100644 db/migrate/20110921192501_create_issues.rb create mode 100644 db/migrate/20110922110156_add_code_to_project.rb create mode 100644 db/migrate/20110923211333_add_status_to_issue.rb create mode 100644 db/migrate/20110924214549_create_rails_admin_histories_table.rb create mode 100644 db/migrate/20110924215658_add_admin_field_to_user.rb create mode 100644 db/migrate/20110926082616_remove_admin.rb create mode 100644 db/migrate/20110927130352_create_notes.rb create mode 100644 db/migrate/20110928140106_add_project_id_for_note.rb create mode 100644 db/migrate/20110928142747_change_noteable_id_for_note.rb create mode 100644 db/migrate/20110928161328_add_attachment_to_note.rb create mode 100644 db/migrate/20111005193700_add_allow_repo_creation_for_user.rb create mode 100644 db/migrate/20111009101738_add_ownerto_project.rb create mode 100644 db/migrate/20111009110913_add_projects_limit_to_user.rb create mode 100644 db/migrate/20111009111204_remove_allow_create_repo_from_user.rb create mode 100644 db/pkey.example create mode 100644 db/schema.rb create mode 100644 db/seeds.rb create mode 100644 doc/README_FOR_APP create mode 100644 install/prepare.rb create mode 100644 lib/assets/.gitkeep create mode 100644 lib/file_size_validator.rb create mode 100644 lib/gitosis.rb create mode 100644 lib/tasks/.gitkeep create mode 100644 lib/utils.rb create mode 100644 log/.gitkeep create mode 100644 public/404.html create mode 100644 public/422.html create mode 100644 public/500.html create mode 100644 public/favicon.ico create mode 100644 public/gitosis_error.html create mode 100644 public/index.html.example create mode 100644 public/robots.txt create mode 100755 script/rails create mode 100644 spec/factories.rb create mode 100644 spec/factory.rb create mode 100644 spec/models/issue_spec.rb create mode 100644 spec/models/key_spec.rb create mode 100644 spec/models/note_spec.rb create mode 100644 spec/models/project_security_spec.rb create mode 100644 spec/models/project_spec.rb create mode 100644 spec/models/user_spec.rb create mode 100644 spec/models/users_project_spec.rb create mode 100644 spec/monkeypatch.rb create mode 100644 spec/requests/admin/admin_projects_spec.rb create mode 100644 spec/requests/admin/admin_users_spec.rb create mode 100644 spec/requests/admin/security_spec.rb create mode 100644 spec/requests/commits_notes_spec.rb create mode 100644 spec/requests/commits_spec.rb create mode 100644 spec/requests/issues_notes_spec.rb create mode 100644 spec/requests/issues_spec.rb create mode 100644 spec/requests/keys_spec.rb create mode 100644 spec/requests/profile_spec.rb create mode 100644 spec/requests/projects_security_spec.rb create mode 100644 spec/requests/projects_spec.rb create mode 100644 spec/requests/projects_tree_spec.rb create mode 100644 spec/requests/projects_wall_spec.rb create mode 100644 spec/requests/team_members_spec.rb create mode 100644 spec/requests/top_panel_spec.rb create mode 100644 spec/requests/user_security_spec.rb create mode 100644 spec/seed_project.tar.gz create mode 100644 spec/spec_helper.rb create mode 100644 spec/support/js_patch.rb create mode 100644 spec/support/login.rb create mode 100644 spec/support/matchers.rb create mode 100644 spec/support/security.rb create mode 100644 spec/support/shared_examples.rb create mode 100644 spec/support/valid_commit.rb create mode 100644 vendor/assets/stylesheets/.gitkeep create mode 100644 vendor/assets/stylesheets/blueprint/ie.css create mode 100755 vendor/assets/stylesheets/blueprint/plugins/buttons/icons/cross.png create mode 100755 vendor/assets/stylesheets/blueprint/plugins/buttons/icons/key.png create mode 100755 vendor/assets/stylesheets/blueprint/plugins/buttons/icons/tick.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/buttons/readme.txt create mode 100644 vendor/assets/stylesheets/blueprint/plugins/buttons/screen.css create mode 100644 vendor/assets/stylesheets/blueprint/plugins/fancy-type/readme.txt create mode 100644 vendor/assets/stylesheets/blueprint/plugins/fancy-type/screen.css create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/doc.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/email.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/external.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/feed.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/im.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/lock.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/pdf.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/visited.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/xls.png create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/readme.txt create mode 100644 vendor/assets/stylesheets/blueprint/plugins/link-icons/screen.css create mode 100644 vendor/assets/stylesheets/blueprint/plugins/rtl/readme.txt create mode 100644 vendor/assets/stylesheets/blueprint/plugins/rtl/screen.css create mode 100644 vendor/assets/stylesheets/blueprint/print.css create mode 100644 vendor/assets/stylesheets/blueprint/screen.css create mode 100644 vendor/assets/stylesheets/blueprint/src/forms.css create mode 100755 vendor/assets/stylesheets/blueprint/src/grid.css create mode 100644 vendor/assets/stylesheets/blueprint/src/grid.png create mode 100644 vendor/assets/stylesheets/blueprint/src/ie.css create mode 100755 vendor/assets/stylesheets/blueprint/src/print.css create mode 100755 vendor/assets/stylesheets/blueprint/src/reset.css create mode 100644 vendor/assets/stylesheets/blueprint/src/typography.css create mode 100644 vendor/assets/stylesheets/jquery_ui/jquery-ui-1.8.16.custom.css create mode 100644 vendor/plugins/.gitkeep diff --git a/.rails_footnotes b/.rails_footnotes new file mode 100644 index 00000000000..1019a70aa1b --- /dev/null +++ b/.rails_footnotes @@ -0,0 +1,3 @@ +#this code temporarily disables notes for all controllers +# Footnotes::Filter.notes = [] + diff --git a/.rspec b/.rspec new file mode 100644 index 00000000000..53607ea52b7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--colour diff --git a/.rvmrc b/.rvmrc new file mode 100644 index 00000000000..8ad3b66d530 --- /dev/null +++ b/.rvmrc @@ -0,0 +1 @@ +rvm use 1.9.2-p290 diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000000..121ae39855d --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,28 @@ +v 1.0.0 + - bug fix + - projects preview mode +v 0.9.6 + - css fix + - new repo empty tree until restart server - fixed +v 0.9.4 + - security improved + - authorization improved + - html escaping + - bug fix + - increassed test coverage + - design improvements + +v 0.9.1 + - increassed test coverage + - design improvements + - new issue email notification + - updated app name + - issue redesigned + - issue can be edit +v 0.8.0 + - sytax highlight for main file types + - redesign + - stability + - security fixes + - increased test coverage + - email notification diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000000..353af0e190c --- /dev/null +++ b/Gemfile @@ -0,0 +1,50 @@ +source 'http://rubygems.org' + +gem 'rails', '3.1.0' + +gem 'sqlite3' +gem 'devise', "1.4.7" +gem 'stamp' +gem 'will_paginate', '~> 3.0' +gem 'haml-rails' +gem 'jquery-rails' +gem 'grit', :git => 'git://github.com/gitlabhq/grit.git' +gem "carrierwave" +gem 'six' +gem 'therubyracer' +gem 'faker' +gem 'seed-fu', :branch => 'rails-3-1', :git => 'git://github.com/mbleigh/seed-fu.git' +gem "inifile" +gem "albino", :git => "git://github.com/gitlabhq/albino.git" +gem "kaminari" +gem "thin" +gem "git" + +group :assets do + gem 'sass-rails', " ~> 3.1.0" + gem 'coffee-rails', "~> 3.1.0" + gem 'uglifier' +end + +group :development do + gem 'rails-footnotes', '>= 3.7.5.rc4' + gem 'annotate', :git => 'git://github.com/ctran/annotate_models.git' +end + +group :development, :test do + gem 'rspec-rails' + gem 'shoulda' + gem 'capybara' + gem 'autotest' + gem 'autotest-rails' + gem 'ruby-debug19', :require => 'ruby-debug' + gem 'awesome_print' + gem 'database_cleaner' + gem 'launchy' +end + + +group :test do + gem 'turn', :require => false + gem 'simplecov', :require => false +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000000..46c908a1d62 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,277 @@ +GIT + remote: git://github.com/ctran/annotate_models.git + revision: cfeec96c9ca0fa5035b10be3d73e798cc4fc52f7 + specs: + annotate (2.4.1.beta1) + +GIT + remote: git://github.com/gitlabhq/albino.git + revision: 118380924969f3a856659f86ea1f40c1ba7bfcb1 + specs: + albino (1.3.3) + posix-spawn (>= 0.3.6) + +GIT + remote: git://github.com/gitlabhq/grit.git + revision: ff015074ef35bd94cba943f9c0f98e161ab5851c + specs: + grit (2.4.1) + diff-lcs (~> 1.1) + mime-types (~> 1.15) + posix-spawn (~> 0.3.6) + +GIT + remote: git://github.com/mbleigh/seed-fu.git + revision: 29fe8c61ca6cc4408115ea7475fe2647081bd348 + branch: rails-3-1 + specs: + seed-fu (2.0.1.rails31) + activerecord (~> 3.1.0.rc4) + activesupport (~> 3.1.0.rc4) + +GEM + remote: http://rubygems.org/ + specs: + ZenTest (4.5.0) + actionmailer (3.1.0) + actionpack (= 3.1.0) + mail (~> 2.3.0) + actionpack (3.1.0) + activemodel (= 3.1.0) + activesupport (= 3.1.0) + builder (~> 3.0.0) + erubis (~> 2.7.0) + i18n (~> 0.6) + rack (~> 1.3.2) + rack-cache (~> 1.0.3) + rack-mount (~> 0.8.2) + rack-test (~> 0.6.1) + sprockets (~> 2.0.0) + activemodel (3.1.0) + activesupport (= 3.1.0) + bcrypt-ruby (~> 3.0.0) + builder (~> 3.0.0) + i18n (~> 0.6) + activerecord (3.1.0) + activemodel (= 3.1.0) + activesupport (= 3.1.0) + arel (~> 2.2.1) + tzinfo (~> 0.3.29) + activeresource (3.1.0) + activemodel (= 3.1.0) + activesupport (= 3.1.0) + activesupport (3.1.0) + multi_json (~> 1.0) + addressable (2.2.6) + ansi (1.3.0) + archive-tar-minitar (0.5.2) + arel (2.2.1) + autotest (4.4.6) + ZenTest (>= 4.4.1) + autotest-rails (4.1.1) + ZenTest (= 4.5) + awesome_print (0.4.0) + bcrypt-ruby (3.0.1) + builder (3.0.0) + capybara (1.0.1) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + selenium-webdriver (~> 2.0) + xpath (~> 0.1.4) + carrierwave (0.5.7) + activesupport (~> 3.0) + childprocess (0.2.2) + ffi (~> 1.0.6) + coffee-rails (3.1.1) + coffee-script (>= 2.2.0) + railties (~> 3.1.0) + coffee-script (2.2.0) + coffee-script-source + execjs + coffee-script-source (1.1.2) + columnize (0.3.4) + daemons (1.1.4) + database_cleaner (0.6.7) + devise (1.4.7) + bcrypt-ruby (~> 3.0) + orm_adapter (~> 0.0.3) + warden (~> 1.0.3) + diff-lcs (1.1.3) + erubis (2.7.0) + eventmachine (0.12.10) + execjs (1.2.6) + multi_json (~> 1.0) + faker (0.9.5) + i18n (~> 0.4) + ffi (1.0.9) + git (1.2.5) + haml (3.1.3) + haml-rails (0.3.4) + actionpack (~> 3.0) + activesupport (~> 3.0) + haml (~> 3.0) + railties (~> 3.0) + hike (1.2.1) + i18n (0.6.0) + inifile (0.4.1) + jquery-rails (1.0.14) + railties (~> 3.0) + thor (~> 0.14) + json_pure (1.5.4) + spruz (~> 0.2.8) + kaminari (0.12.4) + rails (>= 3.0.0) + launchy (2.0.5) + addressable (~> 2.2.6) + libv8 (3.3.10.2) + linecache19 (0.5.12) + ruby_core_source (>= 0.1.4) + mail (2.3.0) + i18n (>= 0.4.0) + mime-types (~> 1.16) + treetop (~> 1.4.8) + mime-types (1.16) + multi_json (1.0.3) + nokogiri (1.5.0) + orm_adapter (0.0.5) + polyglot (0.3.2) + posix-spawn (0.3.6) + rack (1.3.2) + rack-cache (1.0.3) + rack (>= 0.4) + rack-mount (0.8.3) + rack (>= 1.0.0) + rack-ssl (1.3.2) + rack + rack-test (0.6.1) + rack (>= 1.0) + rails (3.1.0) + actionmailer (= 3.1.0) + actionpack (= 3.1.0) + activerecord (= 3.1.0) + activeresource (= 3.1.0) + activesupport (= 3.1.0) + bundler (~> 1.0) + railties (= 3.1.0) + rails-footnotes (3.7.5.rc4) + rails (>= 3.0.0) + railties (3.1.0) + actionpack (= 3.1.0) + activesupport (= 3.1.0) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (~> 0.14.6) + rake (0.9.2) + rdoc (3.9.4) + rspec (2.6.0) + rspec-core (~> 2.6.0) + rspec-expectations (~> 2.6.0) + rspec-mocks (~> 2.6.0) + rspec-core (2.6.4) + rspec-expectations (2.6.0) + diff-lcs (~> 1.1.2) + rspec-mocks (2.6.0) + rspec-rails (2.6.1) + actionpack (~> 3.0) + activesupport (~> 3.0) + railties (~> 3.0) + rspec (~> 2.6.0) + ruby-debug-base19 (0.11.25) + columnize (>= 0.3.1) + linecache19 (>= 0.5.11) + ruby_core_source (>= 0.1.4) + ruby-debug19 (0.11.6) + columnize (>= 0.3.1) + linecache19 (>= 0.5.11) + ruby-debug-base19 (>= 0.11.19) + ruby_core_source (0.1.5) + archive-tar-minitar (>= 0.5.2) + rubyzip (0.9.4) + sass (3.1.7) + sass-rails (3.1.1) + actionpack (~> 3.1.0) + railties (~> 3.1.0) + sass (>= 3.1.4) + tilt (~> 1.3.2) + selenium-webdriver (2.5.0) + childprocess (>= 0.2.1) + ffi (>= 1.0.7) + json_pure + rubyzip + shoulda (2.11.3) + simplecov (0.5.3) + multi_json (~> 1.0.3) + simplecov-html (~> 0.5.3) + simplecov-html (0.5.3) + six (0.2.0) + sprockets (2.0.0) + hike (~> 1.2) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + spruz (0.2.13) + sqlite3 (1.3.4) + stamp (0.1.6) + therubyracer (0.9.4) + libv8 (~> 3.3.10) + thin (1.2.11) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) + thor (0.14.6) + tilt (1.3.3) + treetop (1.4.10) + polyglot + polyglot (>= 0.3.1) + turn (0.8.2) + ansi (>= 1.2.2) + tzinfo (0.3.29) + uglifier (1.0.3) + execjs (>= 0.3.0) + multi_json (>= 1.0.2) + warden (1.0.5) + rack (>= 1.0) + will_paginate (3.0.0) + xpath (0.1.4) + nokogiri (~> 1.3) + +PLATFORMS + ruby + +DEPENDENCIES + albino! + annotate! + autotest + autotest-rails + awesome_print + capybara + carrierwave + coffee-rails (~> 3.1.0) + database_cleaner + devise (= 1.4.7) + faker + git + grit! + haml-rails + inifile + jquery-rails + kaminari + launchy + rails (= 3.1.0) + rails-footnotes (>= 3.7.5.rc4) + rspec-rails + ruby-debug19 + sass-rails (~> 3.1.0) + seed-fu! + shoulda + simplecov + six + sqlite3 + stamp + therubyracer + thin + turn + uglifier + will_paginate (~> 3.0) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..7cecc2485f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Dmitriy Zaporozhets + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 00000000000..200977877bc --- /dev/null +++ b/README.rdoc @@ -0,0 +1,85 @@ +== Welcome to GitLab + +GitLAb is a free Project/Repository managment application + +== Application details + +rails 3.1 +works only with gitosis +sqlite as default db + + +== Requirements + +* ruby 1.9.2 + +* sqlite + +* git + +* gitosis + +* ubuntu/debian + +* pygments lib - sudo easy_install pygments + + +== Install Project + + + git clone git://github.com/gitlabhq/gitlabhq.git + cd gitlabhq/ + + # install this library first + sudo easy_install pygments + + sudo gem install bundler + bundle + + RAILS_ENV=production rake db:setup + + # create admin user + # login....admin@local.host + # pass.....5iveL!fe + RAILS_ENV=production rake db:seed_fu + +Install gitosis, edit conf/gitosis.yml & start server + + rails s + +== Install Gitosis + sudo aptitude install gitosis + + sudo adduser \ + --system \ + --shell /bin/sh \ + --gecos 'git version control' \ + --group \ + --disabled-password \ + --home /home/git \ + git + + + ssh-keygen -t rsa + + sudo -H -u git gitosis-init < ~/.ssh/id_rsa.pub + sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update + + +== Install ruby 1.9.2 + + sudo aptitude install git-core curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-dev libcurl4-openssl-dev libreadline5-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev + + wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz + + tar xfvz ruby-1.9.2-p290.tar.gz + + cd ruby-1.9.2-p290 + ./configure + make + sudo checkinstall -D + + sudo gem update --system + + + echo "gem: --no-rdoc --no-ri" > ~/.gemrc diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000000..35b2f05cbb4 --- /dev/null +++ b/Rakefile @@ -0,0 +1,7 @@ +#!/usr/bin/env rake +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require File.expand_path('../config/application', __FILE__) + +Gitlab::Application.load_tasks diff --git a/VERSION b/VERSION new file mode 100644 index 00000000000..3eefcb9dd5b --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/app/assets/images/.directory b/app/assets/images/.directory new file mode 100644 index 00000000000..93a51f7c384 --- /dev/null +++ b/app/assets/images/.directory @@ -0,0 +1,4 @@ +[Dolphin] +ShowPreview=true +Timestamp=2011,9,14,20,34,18 +Version=2 diff --git a/app/assets/images/ajax-loader.gif b/app/assets/images/ajax-loader.gif new file mode 100644 index 00000000000..0ca7ada9605 Binary files /dev/null and b/app/assets/images/ajax-loader.gif differ diff --git a/app/assets/images/blueprint_add.png b/app/assets/images/blueprint_add.png new file mode 100644 index 00000000000..0969428c865 Binary files /dev/null and b/app/assets/images/blueprint_add.png differ diff --git a/app/assets/images/blueprint_delete.png b/app/assets/images/blueprint_delete.png new file mode 100644 index 00000000000..9b7f5c66007 Binary files /dev/null and b/app/assets/images/blueprint_delete.png differ diff --git a/app/assets/images/blueprint_info.png b/app/assets/images/blueprint_info.png new file mode 100644 index 00000000000..8278b9da7af Binary files /dev/null and b/app/assets/images/blueprint_info.png differ diff --git a/app/assets/images/blueprint_notice.png b/app/assets/images/blueprint_notice.png new file mode 100644 index 00000000000..e00264986ac Binary files /dev/null and b/app/assets/images/blueprint_notice.png differ diff --git a/app/assets/images/dir.png b/app/assets/images/dir.png new file mode 100644 index 00000000000..bd941249c87 Binary files /dev/null and b/app/assets/images/dir.png differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 00000000000..057f74ac7ab Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png new file mode 100644 index 00000000000..310003b8dec Binary files /dev/null and b/app/assets/images/favicon.png differ diff --git a/app/assets/images/git.png b/app/assets/images/git.png new file mode 100644 index 00000000000..cb5d0590efa Binary files /dev/null and b/app/assets/images/git.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_flat_0_aaaaaa_40x100.png b/app/assets/images/jquery_ui/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 00000000000..5b5dab2ab7b Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_flat_75_ffffff_40x100.png b/app/assets/images/jquery_ui/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 00000000000..ac8b229af95 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_flat_75_ffffff_40x100.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_glass_55_fbf9ee_1x400.png b/app/assets/images/jquery_ui/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 00000000000..ad3d6346e00 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_glass_65_ffffff_1x400.png b/app/assets/images/jquery_ui/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 00000000000..42ccba269b6 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_glass_75_dadada_1x400.png b/app/assets/images/jquery_ui/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 00000000000..5a46b47cb16 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_glass_75_dadada_1x400.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_glass_75_e6e6e6_1x400.png b/app/assets/images/jquery_ui/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 00000000000..86c2baa655e Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_glass_95_fef1ec_1x400.png b/app/assets/images/jquery_ui/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 00000000000..4443fdc1a15 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/app/assets/images/jquery_ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/app/assets/images/jquery_ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 00000000000..7c9fa6c6edc Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/app/assets/images/jquery_ui/images/ui-icons_222222_256x240.png b/app/assets/images/jquery_ui/images/ui-icons_222222_256x240.png new file mode 100644 index 00000000000..b273ff111d2 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-icons_222222_256x240.png differ diff --git a/app/assets/images/jquery_ui/images/ui-icons_2e83ff_256x240.png b/app/assets/images/jquery_ui/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 00000000000..84defe6e8ab Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-icons_2e83ff_256x240.png differ diff --git a/app/assets/images/jquery_ui/images/ui-icons_454545_256x240.png b/app/assets/images/jquery_ui/images/ui-icons_454545_256x240.png new file mode 100644 index 00000000000..59bd45b907c Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-icons_454545_256x240.png differ diff --git a/app/assets/images/jquery_ui/images/ui-icons_888888_256x240.png b/app/assets/images/jquery_ui/images/ui-icons_888888_256x240.png new file mode 100644 index 00000000000..6d02426c114 Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-icons_888888_256x240.png differ diff --git a/app/assets/images/jquery_ui/images/ui-icons_cd0a0a_256x240.png b/app/assets/images/jquery_ui/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 00000000000..2ab019b73ec Binary files /dev/null and b/app/assets/images/jquery_ui/images/ui-icons_cd0a0a_256x240.png differ diff --git a/app/assets/images/list_view_icon.jpg b/app/assets/images/list_view_icon.jpg new file mode 100644 index 00000000000..45a34cb465f Binary files /dev/null and b/app/assets/images/list_view_icon.jpg differ diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png new file mode 100644 index 00000000000..99e5fd9b436 Binary files /dev/null and b/app/assets/images/no_avatar.png differ diff --git a/app/assets/images/rails.png b/app/assets/images/rails.png new file mode 100644 index 00000000000..d5edc04e65f Binary files /dev/null and b/app/assets/images/rails.png differ diff --git a/app/assets/images/txt.png b/app/assets/images/txt.png new file mode 100644 index 00000000000..f3638cb4e1e Binary files /dev/null and b/app/assets/images/txt.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 00000000000..01e3b416526 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,18 @@ +// This is a manifest file that'll be compiled into including all the files listed below. +// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +// be included in the compiled file accessible from http://example.com/assets/application.js +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// the compiled file. +// +//= require jquery +//= require jquery_ujs +//= require_tree . + +$(function(){ + $(".one_click_select").click(function(){ + $(this).select(); + }); + + $('select#branch').selectmenu({style:'popup', width:200}); + $('select#tag').selectmenu({style:'popup', width:200}); +}); diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js new file mode 100644 index 00000000000..a62e9330ffc --- /dev/null +++ b/app/assets/javascripts/commits.js @@ -0,0 +1,9 @@ +$(document).ready(function(){ + $(".day-commits-table li.commit").live('click', function(e){ + if(e.target.nodeName != "A") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); +}); diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee new file mode 100644 index 00000000000..761567942fc --- /dev/null +++ b/app/assets/javascripts/dashboard.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee new file mode 100644 index 00000000000..761567942fc --- /dev/null +++ b/app/assets/javascripts/issues.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/jquery-ui-1.8.16.custom.min.js b/app/assets/javascripts/jquery-ui-1.8.16.custom.min.js new file mode 100644 index 00000000000..14c9064f7f2 --- /dev/null +++ b/app/assets/javascripts/jquery-ui-1.8.16.custom.min.js @@ -0,0 +1,791 @@ +/*! + * jQuery UI 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16", +keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d= +this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this, +"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart": +"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight, +outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a, +"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&& +a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= +false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +;/* + * jQuery UI Position 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Position + */ +(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY, +left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+= +k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-= +m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left= +d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= +a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), +g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); +;/* + * jQuery UI Draggable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options; +this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); +this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true}, +_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b= +false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration, +10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle|| +!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&& +a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent= +this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"), +10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"), +10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top, +(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!= +"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"), +10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+ +this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&& +!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= +i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), +top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= +this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", +nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== +String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy(); +var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a= +false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"}); +this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff= +{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis]; +if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false}, +_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f, +{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight: +Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(cb.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left= +null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+ +a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+ +c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]); +b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(), +10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top- +f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType? +e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a= +e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing, +step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement= +e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset; +var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left: +a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top- +d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition, +f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25, +display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b= +e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height= +d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery); +;/* + * jQuery UI Selectable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), +selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, +c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", +c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= +this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable"); +this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a=== +"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&& +!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top, +left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; +this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!= +document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a); +return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0], +e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset(); +c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): +this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null, +dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")}, +toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith(); +if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), +this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b= +this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f= +d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")|| +0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out", +a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h- +f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g- +this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this, +this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop", +a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); +a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); +if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion", +function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a= +this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); +this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); +b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); +a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ +c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options; +if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); +if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(), +e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight|| +e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false", +"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16", +animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/); +f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide", +paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); +;/* + * jQuery UI Autocomplete 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + */ +(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g= +false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!= +a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)}; +this.menu=d("
    ").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&& +a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); +d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&& +b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source= +this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(d("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, +"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery); +(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); +this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b, +this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| +this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| +this.first()?":last":":first"))},hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("");e.secondary&&a.append("");if(!this.options.text){d.push(f?"ui-button-icons-only": +"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")=== +"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"); +b.Widget.prototype.destroy.call(this)}})})(jQuery); +;/* + * jQuery UI Dialog 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.button.js + * jquery.ui.draggable.js + * jquery.ui.mouse.js + * jquery.ui.position.js + * jquery.ui.resizable.js + */ +(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false, +position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ +b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), +h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", +e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); +a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== +b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1; +d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== +f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a, +function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close", +handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition, +originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize", +f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "): +[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f); +if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"): +e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a= +this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height- +b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "), +create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&& +c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j"); +this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle", +g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length? +(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i- +m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy(); +return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false; +this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b= +this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b= +this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b); +c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a= +this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e- +g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"}, +b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery); +;/* + * jQuery UI Tabs 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& +e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= +d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| +(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); +this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ +g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", +function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; +this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= +-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; +d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= +d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, +e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); +j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); +if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, +this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, +load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, +"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, +url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k'))}function N(a){return a.bind("mouseout", +function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); +b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv}, +setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g, +"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('
    '))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker", +function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c== +"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f==""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker(): +d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a, +b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+= +1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/ +2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b= +d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e= +a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a, +"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f== +a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input", +a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value= +"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b); +c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing= +true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}); +a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&& +!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(), +h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b= +this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b); +this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, +_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): +0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"? +"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a); +this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField"); +if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"? +b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd", +COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames: +null)||this._defaults.monthNames;var i=function(o){(o=k+1 +12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&& +a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? +new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a)); +n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m, +g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+s+"":f?"":''+s+"";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&& +a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,s)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='
    '+(/all|left/.test(t)&& +x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'
    ';var z=j?'":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="=5?' class="ui-datepicker-week-end"':"")+'>'+q[r]+""}y+=z+"";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, +z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q";var R=!j?"":'";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&ro;R+='";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+""}g++;if(g>11){g=0;m++}y+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(r)+""+(F&&!D?" ":L?''+ +r.getDate()+"":''+r.getDate()+"")+"
    "+(l?""+(i[0]>0&&G==i[1]-1?'
    ':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': +"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b, +e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+ +(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input? +a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c, +e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a, +"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this; +if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a== +"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery); +;/* + * jQuery UI Progressbar 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* +this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery); +;/* + * jQuery UI Effects 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/ + */ +jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], +16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, +a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= +a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", +"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, +0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, +211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, +d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; +f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, +[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}), +d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement; +if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)}); +return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this, +arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/ +2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b, +d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c, +a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b, +d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +;/* + * jQuery UI Effects Fade 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fade + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Fold 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], +10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +;/* + * jQuery UI Effects Highlight 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Pulsate 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); +; \ No newline at end of file diff --git a/app/assets/javascripts/jquery.cookie.js b/app/assets/javascripts/jquery.cookie.js new file mode 100644 index 00000000000..6a3e394b403 --- /dev/null +++ b/app/assets/javascripts/jquery.cookie.js @@ -0,0 +1,41 @@ +/** + * jQuery Cookie plugin + * + * Copyright (c) 2010 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +jQuery.cookie = function (key, value, options) { + + // key and at least value given, set cookie... + if (arguments.length > 1 && String(value) !== "[object Object]") { + options = jQuery.extend({}, options); + + if (value === null || value === undefined) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', + options.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // key and possibly options given, get cookie... + options = value || {}; + var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent; + return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null; +}; diff --git a/app/assets/javascripts/jquery.ui.selectmenu.js b/app/assets/javascripts/jquery.ui.selectmenu.js new file mode 100644 index 00000000000..d61d75f96ab --- /dev/null +++ b/app/assets/javascripts/jquery.ui.selectmenu.js @@ -0,0 +1,845 @@ + /* + * jQuery UI selectmenu dev version + * + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI + * https://github.com/fnagel/jquery-ui/wiki/Selectmenu + */ + +(function($) { + +$.widget("ui.selectmenu", { + getter: "value", + version: "1.8", + eventPrefix: "selectmenu", + options: { + transferClasses: true, + typeAhead: "sequential", + style: 'dropdown', + positionOptions: { + my: "left top", + at: "left bottom", + offset: null + }, + width: null, + menuWidth: null, + handleWidth: 26, + maxHeight: null, + icons: null, + format: null, + bgImage: function() {}, + wrapperElement: "
    " + }, + + _create: function() { + var self = this, o = this.options; + + // set a default id value, generate a new random one if not set by developer + var selectmenuId = this.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 ); + + // quick array of button and menu id's + this.ids = [ selectmenuId + '-button', selectmenuId + '-menu' ]; + + // define safe mouseup for future toggling + this._safemouseup = true; + + // create menu button wrapper + this.newelement = $( '', { + 'class': this.widgetBaseClass + ' ui-widget ui-state-default ui-corner-all', + 'id' : this.ids[ 0 ], + 'role': 'button', + 'href': '#nogo', + 'tabindex': this.element.attr( 'disabled' ) ? 1 : 0, + 'aria-haspopup': true, + 'aria-owns': this.ids[ 1 ] + }); + this.newelementWrap = $( o.wrapperElement ) + .append( this.newelement ) + .insertAfter( this.element ); + + // transfer tabindex + var tabindex = this.element.attr( 'tabindex' ); + if ( tabindex ) { + this.newelement.attr( 'tabindex', tabindex ); + } + + // save reference to select in data for ease in calling methods + this.newelement.data( 'selectelement', this.element ); + + // menu icon + this.selectmenuIcon = $( '' ) + .prependTo( this.newelement ); + + // append status span to button + this.newelement.prepend( '' ); + + // make associated form label trigger focus + $( 'label[for="' + selectmenuId + '"]' ) + .attr( 'for', this.ids[0] ) + .bind( 'click.selectmenu', function() { + self.newelement[0].focus(); + return false; + }); + + // click toggle for menu visibility + this.newelement + .bind('mousedown.selectmenu', function(event) { + self._toggle(event, true); + // make sure a click won't open/close instantly + if (o.style == "popup") { + self._safemouseup = false; + setTimeout(function() { self._safemouseup = true; }, 300); + } + return false; + }) + .bind('click.selectmenu', function() { + return false; + }) + .bind("keydown.selectmenu", function(event) { + var ret = false; + switch (event.keyCode) { + case $.ui.keyCode.ENTER: + ret = true; + break; + case $.ui.keyCode.SPACE: + self._toggle(event); + break; + case $.ui.keyCode.UP: + if (event.altKey) { + self.open(event); + } else { + self._moveSelection(-1); + } + break; + case $.ui.keyCode.DOWN: + if (event.altKey) { + self.open(event); + } else { + self._moveSelection(1); + } + break; + case $.ui.keyCode.LEFT: + self._moveSelection(-1); + break; + case $.ui.keyCode.RIGHT: + self._moveSelection(1); + break; + case $.ui.keyCode.TAB: + ret = true; + break; + default: + ret = true; + } + return ret; + }) + .bind('keypress.selectmenu', function(event) { + self._typeAhead(event.which, 'mouseup'); + return true; + }) + .bind('mouseover.selectmenu focus.selectmenu', function() { + if (!o.disabled) { + $(this).addClass(self.widgetBaseClass + '-focus ui-state-hover'); + } + }) + .bind('mouseout.selectmenu blur.selectmenu', function() { + if (!o.disabled) { + $(this).removeClass(self.widgetBaseClass + '-focus ui-state-hover'); + } + }); + + // document click closes menu + $(document).bind("mousedown.selectmenu", function(event) { + self.close(event); + }); + + // change event on original selectmenu + this.element + .bind("click.selectmenu", function() { + self._refreshValue(); + }) + // FIXME: newelement can be null under unclear circumstances in IE8 + // TODO not sure if this is still a problem (fnagel 20.03.11) + .bind("focus.selectmenu", function() { + if (self.newelement) { + self.newelement[0].focus(); + } + }); + + // set width when not set via options + if (!o.width) { + o.width = this.element.outerWidth(); + } + // set menu button width + this.newelement.width(o.width); + + // hide original selectmenu element + this.element.hide(); + + // create menu portion, append to body + this.list = $( '
      ', { + 'class': 'ui-widget ui-widget-content', + 'aria-hidden': true, + 'role': 'listbox', + 'aria-labelledby': this.ids[0], + 'id': this.ids[1] + }); + this.listWrap = $( o.wrapperElement ) + .addClass( self.widgetBaseClass + '-menu' ) + .append( this.list ) + .appendTo( 'body' ); + + // transfer menu click to menu button + this.list + .bind("keydown.selectmenu", function(event) { + var ret = false; + switch (event.keyCode) { + case $.ui.keyCode.UP: + if (event.altKey) { + self.close(event, true); + } else { + self._moveFocus(-1); + } + break; + case $.ui.keyCode.DOWN: + if (event.altKey) { + self.close(event, true); + } else { + self._moveFocus(1); + } + break; + case $.ui.keyCode.LEFT: + self._moveFocus(-1); + break; + case $.ui.keyCode.RIGHT: + self._moveFocus(1); + break; + case $.ui.keyCode.HOME: + self._moveFocus(':first'); + break; + case $.ui.keyCode.PAGE_UP: + self._scrollPage('up'); + break; + case $.ui.keyCode.PAGE_DOWN: + self._scrollPage('down'); + break; + case $.ui.keyCode.END: + self._moveFocus(':last'); + break; + case $.ui.keyCode.ENTER: + case $.ui.keyCode.SPACE: + self.close(event, true); + $(event.target).parents('li:eq(0)').trigger('mouseup'); + break; + case $.ui.keyCode.TAB: + ret = true; + self.close(event, true); + $(event.target).parents('li:eq(0)').trigger('mouseup'); + break; + case $.ui.keyCode.ESCAPE: + self.close(event, true); + break; + default: + ret = true; + } + return ret; + }) + .bind('keypress.selectmenu', function(event) { + self._typeAhead(event.which, 'focus'); + return true; + }) + // this allows for using the scrollbar in an overflowed list + .bind( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; }); + + // needed when window is resized + // TODO seems to be useless, but causes errors (fnagel 01.08.11) + // see: https://github.com/fnagel/jquery-ui/issues/147 + // $(window).bind( "resize.selectmenu", $.proxy( self._refreshPosition, this ) ); + }, + + _init: function() { + var self = this, o = this.options; + + // serialize selectmenu element options + var selectOptionData = []; + this.element + .find('option') + .each(function() { + var opt = $(this); + selectOptionData.push({ + value: opt.attr('value'), + text: self._formatText(opt.text()), + selected: opt.attr('selected'), + disabled: opt.attr('disabled'), + classes: opt.attr('class'), + typeahead: opt.attr('typeahead'), + parentOptGroup: opt.parent('optgroup'), + bgImage: o.bgImage.call(opt) + }); + }); + + // active state class is only used in popup style + var activeClass = (self.options.style == "popup") ? " ui-state-active" : ""; + + // empty list so we can refresh the selectmenu via selectmenu() + this.list.html(""); + + // write li's + if (selectOptionData.length) { + for (var i = 0; i < selectOptionData.length; i++) { + var thisLiAttr = { role : 'presentation' }; + if ( selectOptionData[ i ].disabled ) { + thisLiAttr[ 'class' ] = this.namespace + '-state-disabled'; + } + var thisAAttr = { + html: selectOptionData[i].text, + href : '#nogo', + tabindex : -1, + role : 'option', + 'aria-selected' : false + }; + if ( selectOptionData[ i ].disabled ) { + thisAAttr[ 'aria-disabled' ] = selectOptionData[ i ].disabled; + } + if ( selectOptionData[ i ].typeahead ) { + thisAAttr[ 'typeahead' ] = selectOptionData[ i ].typeahead; + } + var thisA = $('', thisAAttr); + var thisLi = $('
    • ', thisLiAttr) + .append(thisA) + .data('index', i) + .addClass(selectOptionData[i].classes) + .data('optionClasses', selectOptionData[i].classes || '') + .bind("mouseup.selectmenu", function(event) { + if (self._safemouseup && !self._disabled(event.currentTarget) && !self._disabled($( event.currentTarget ).parents( "ul>li." + self.widgetBaseClass + "-group " )) ) { + var changed = $(this).data('index') != self._selectedIndex(); + self.index($(this).data('index')); + self.select(event); + if (changed) { + self.change(event); + } + self.close(event, true); + } + return false; + }) + .bind("click.selectmenu", function() { + return false; + }) + .bind('mouseover.selectmenu focus.selectmenu', function(e) { + // no hover if diabled + if (!$(e.currentTarget).hasClass(self.namespace + '-state-disabled') && !$(e.currentTarget).parent("ul").parent("li").hasClass(self.namespace + '-state-disabled')) { + self._selectedOptionLi().addClass(activeClass); + self._focusedOptionLi().removeClass(self.widgetBaseClass + '-item-focus ui-state-hover'); + $(this).removeClass('ui-state-active').addClass(self.widgetBaseClass + '-item-focus ui-state-hover'); + } + }) + .bind('mouseout.selectmenu blur.selectmenu', function() { + if ($(this).is(self._selectedOptionLi().selector)) { + $(this).addClass(activeClass); + } + $(this).removeClass(self.widgetBaseClass + '-item-focus ui-state-hover'); + }); + + // optgroup or not... + if ( selectOptionData[i].parentOptGroup.length ) { + var optGroupName = self.widgetBaseClass + '-group-' + this.element.find( 'optgroup' ).index( selectOptionData[i].parentOptGroup ); + if (this.list.find( 'li.' + optGroupName ).length ) { + this.list.find( 'li.' + optGroupName + ':last ul' ).append( thisLi ); + } else { + $('
    • ') + .appendTo( this.list ) + .find( 'ul' ) + .append( thisLi ); + } + } else { + thisLi.appendTo(this.list); + } + + // append icon if option is specified + if (o.icons) { + for (var j in o.icons) { + if (thisLi.is(o.icons[j].find)) { + thisLi + .data('optionClasses', selectOptionData[i].classes + ' ' + self.widgetBaseClass + '-hasIcon') + .addClass(self.widgetBaseClass + '-hasIcon'); + var iconClass = o.icons[j].icon || ""; + thisLi + .find('a:eq(0)') + .prepend(''); + if (selectOptionData[i].bgImage) { + thisLi.find('span').css('background-image', selectOptionData[i].bgImage); + } + } + } + } + } + } else { + $('
    • ').appendTo(this.list); + } + // we need to set and unset the CSS classes for dropdown and popup style + var isDropDown = ( o.style == 'dropdown' ); + this.newelement + .toggleClass( self.widgetBaseClass + '-dropdown', isDropDown ) + .toggleClass( self.widgetBaseClass + '-popup', !isDropDown ); + this.list + .toggleClass( self.widgetBaseClass + '-menu-dropdown ui-corner-bottom', isDropDown ) + .toggleClass( self.widgetBaseClass + '-menu-popup ui-corner-all', !isDropDown ) + // add corners to top and bottom menu items + .find( 'li:first' ) + .toggleClass( 'ui-corner-top', !isDropDown ) + .end().find( 'li:last' ) + .addClass( 'ui-corner-bottom' ); + this.selectmenuIcon + .toggleClass( 'ui-icon-triangle-1-s', isDropDown ) + .toggleClass( 'ui-icon-triangle-2-n-s', !isDropDown ); + + // transfer classes to selectmenu and list + if ( o.transferClasses ) { + var transferClasses = this.element.attr( 'class' ) || ''; + this.newelement.add( this.list ).addClass( transferClasses ); + } + + // set menu width to either menuWidth option value, width option value, or select width + if ( o.style == 'dropdown' ) { + this.list.width( o.menuWidth ? o.menuWidth : o.width ); + } else { + this.list.width( o.menuWidth ? o.menuWidth : o.width - o.handleWidth ); + } + + // reset height to auto + this.list.css( 'height', 'auto' ); + var listH = this.listWrap.height(); + // calculate default max height + if ( o.maxHeight && o.maxHeight < listH ) { + this.list.height( o.maxHeight ); + } else { + var winH = $( window ).height() / 3; + if ( winH < listH ) this.list.height( winH ); + } + + // save reference to actionable li's (not group label li's) + this._optionLis = this.list.find( 'li:not(.' + self.widgetBaseClass + '-group)' ); + + // transfer disabled state + if ( this.element.attr( 'disabled' ) ) { + this.disable(); + } else { + this.enable() + } + + // update value + this.index( this._selectedIndex() ); + + // needed when selectmenu is placed at the very bottom / top of the page + window.setTimeout( function() { + self._refreshPosition(); + }, 200 ); + }, + + destroy: function() { + this.element.removeData( this.widgetName ) + .removeClass( this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled' ) + .removeAttr( 'aria-disabled' ) + .unbind( ".selectmenu" ); + + // TODO unneded as event binding has been disabled + // $( window ).unbind( ".selectmenu" ); + $( document ).unbind( ".selectmenu" ); + + // unbind click on label, reset its for attr + $( 'label[for=' + this.newelement.attr('id') + ']' ) + .attr( 'for', this.element.attr( 'id' ) ) + .unbind( '.selectmenu' ); + + this.newelementWrap.remove(); + this.listWrap.remove(); + + this.element.show(); + + // call widget destroy function + $.Widget.prototype.destroy.apply(this, arguments); + }, + + _typeAhead: function( code, eventType ){ + var self = this, focusFound = false, C = String.fromCharCode(code).toUpperCase(); + c = C.toLowerCase(); + + if ( self.options.typeAhead == 'sequential' ) { + // clear the timeout so we can use _prevChar + window.clearTimeout('ui.selectmenu-' + self.selectmenuId); + + // define our find var + var find = typeof( self._prevChar ) == 'undefined' ? '' : self._prevChar.join( '' ); + + function focusOptSeq( elem, ind, c ){ + focusFound = true; + $( elem ).trigger( eventType ); + typeof( self._prevChar ) == 'undefined' ? self._prevChar = [ c ] : self._prevChar[ self._prevChar.length ] = c; + } + this.list.find( 'li a' ).each( function( i ) { + if ( !focusFound ) { + // allow the typeahead attribute on the option tag for a more specific lookup + var thisText = $( this ).attr( 'typeahead' ) || $(this).text(); + if ( thisText.indexOf( find + C ) === 0 ) { + focusOptSeq( this, i, C ); + } else if (thisText.indexOf(find+c) === 0 ) { + focusOptSeq( this, i, c ); + } + } + }); + // set a 1 second timeout for sequenctial typeahead + // keep this set even if we have no matches so it doesnt typeahead somewhere else + window.setTimeout( function( el ) { + self._prevChar = undefined; + }, 1000, self ); + + } else { + // define self._prevChar if needed + if ( !self._prevChar ) { self._prevChar = [ '' , 0 ]; } + + focusFound = false; + function focusOpt( elem, ind ){ + focusFound = true; + $( elem ).trigger( eventType ); + self._prevChar[ 1 ] = ind; + } + this.list.find( 'li a' ).each(function( i ){ + if (!focusFound){ + var thisText = $(this).text(); + if ( thisText.indexOf( C ) === 0 || thisText.indexOf( c ) === 0 ) { + if (self._prevChar[0] == C){ + if ( self._prevChar[ 1 ] < i ){ focusOpt( this, i ); } + } else{ + focusOpt( this, i ); + } + } + } + }); + this._prevChar[ 0 ] = C; + } + }, + + // returns some usefull information, called by callbacks only + _uiHash: function() { + var index = this.index(); + return { + index: index, + option: $("option", this.element).get(index), + value: this.element[0].value + }; + }, + + open: function(event) { + var self = this, o = this.options; + if ( self.newelement.attr("aria-disabled") != 'true' ) { + self._closeOthers(event); + self.newelement.addClass('ui-state-active'); + + self.listWrap.appendTo( o.appendTo ); + self.list.attr('aria-hidden', false); + + if ( o.style == "dropdown" ) { + self.newelement.removeClass('ui-corner-all').addClass('ui-corner-top'); + } + + self.listWrap.addClass( self.widgetBaseClass + '-open' ); + // positioning needed for IE7 (tested 01.08.11 on MS VPC Image) + // see https://github.com/fnagel/jquery-ui/issues/147 + if ( $.browser.msie && $.browser.version.substr( 0,1 ) == 7 ) { + self._refreshPosition(); + } + var selected = self.list.attr('aria-hidden', false).find('li:not(.' + self.widgetBaseClass + '-group):eq(' + self._selectedIndex() + '):visible a'); + if (selected.length) selected[0].focus(); + // positioning needed for FF, Chrome, IE8, IE7, IE6 (tested 01.08.11 on MS VPC Image) + self._refreshPosition(); + + self._trigger("open", event, self._uiHash()); + } + }, + + close: function(event, retainFocus) { + if ( this.newelement.is('.ui-state-active') ) { + this.newelement + .removeClass('ui-state-active'); + this.listWrap.removeClass(this.widgetBaseClass + '-open'); + this.list.attr('aria-hidden', true); + if ( this.options.style == "dropdown" ) { + this.newelement.removeClass('ui-corner-top').addClass('ui-corner-all'); + } + if ( retainFocus ) { + this.newelement.focus(); + } + this._trigger("close", event, this._uiHash()); + } + }, + + change: function(event) { + this.element.trigger("change"); + this._trigger("change", event, this._uiHash()); + }, + + select: function(event) { + if (this._disabled(event.currentTarget)) { return false; } + this._trigger("select", event, this._uiHash()); + }, + + _closeOthers: function(event) { + $('.' + this.widgetBaseClass + '.ui-state-active').not(this.newelement).each(function() { + $(this).data('selectelement').selectmenu('close', event); + }); + $('.' + this.widgetBaseClass + '.ui-state-hover').trigger('mouseout'); + }, + + _toggle: function(event, retainFocus) { + if ( this.list.parent().is('.' + this.widgetBaseClass + '-open') ) { + this.close(event, retainFocus); + } else { + this.open(event); + } + }, + + _formatText: function(text) { + return (this.options.format ? this.options.format(text) : text); + }, + + _selectedIndex: function() { + return this.element[0].selectedIndex; + }, + + _selectedOptionLi: function() { + return this._optionLis.eq(this._selectedIndex()); + }, + + _focusedOptionLi: function() { + return this.list.find('.' + this.widgetBaseClass + '-item-focus'); + }, + + _moveSelection: function(amt, recIndex) { + // do nothing if disabled + if (!this.options.disabled) { + var currIndex = parseInt(this._selectedOptionLi().data('index') || 0, 10); + var newIndex = currIndex + amt; + // do not loop when using up key + + if (newIndex < 0) { + newIndex = 0; + } + if (newIndex > this._optionLis.size() - 1) { + newIndex = this._optionLis.size() - 1; + } + // Occurs when a full loop has been made + if (newIndex === recIndex) { return false; } + + if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) { + // if option at newIndex is disabled, call _moveFocus, incrementing amt by one + (amt > 0) ? ++amt : --amt; + this._moveSelection(amt, newIndex); + } else { + return this._optionLis.eq(newIndex).trigger('mouseup'); + } + } + }, + + _moveFocus: function(amt, recIndex) { + if (!isNaN(amt)) { + var currIndex = parseInt(this._focusedOptionLi().data('index') || 0, 10); + var newIndex = currIndex + amt; + } else { + var newIndex = parseInt(this._optionLis.filter(amt).data('index'), 10); + } + + if (newIndex < 0) { + newIndex = 0; + } + if (newIndex > this._optionLis.size() - 1) { + newIndex = this._optionLis.size() - 1; + } + + //Occurs when a full loop has been made + if (newIndex === recIndex) { return false; } + + var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000); + + this._focusedOptionLi().find('a:eq(0)').attr('id', ''); + + if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) { + // if option at newIndex is disabled, call _moveFocus, incrementing amt by one + (amt > 0) ? ++amt : --amt; + this._moveFocus(amt, newIndex); + } else { + this._optionLis.eq(newIndex).find('a:eq(0)').attr('id',activeID).focus(); + } + + this.list.attr('aria-activedescendant', activeID); + }, + + _scrollPage: function(direction) { + var numPerPage = Math.floor(this.list.outerHeight() / this.list.find('li:first').outerHeight()); + numPerPage = (direction == 'up' ? -numPerPage : numPerPage); + this._moveFocus(numPerPage); + }, + + _setOption: function(key, value) { + this.options[key] = value; + // set + if (key == 'disabled') { + this.close(); + this.element + .add(this.newelement) + .add(this.list)[value ? 'addClass' : 'removeClass']( + this.widgetBaseClass + '-disabled' + ' ' + + this.namespace + '-state-disabled') + .attr("aria-disabled", value); + } + }, + + disable: function(index, type){ + // if options is not provided, call the parents disable function + if ( typeof( index ) == 'undefined' ) { + this._setOption( 'disabled', true ); + } else { + if ( type == "optgroup" ) { + this._disableOptgroup(index); + } else { + this._disableOption(index); + } + } + }, + + enable: function(index, type) { + // if options is not provided, call the parents enable function + if ( typeof( index ) == 'undefined' ) { + this._setOption('disabled', false); + } else { + if ( type == "optgroup" ) { + this._enableOptgroup(index); + } else { + this._enableOption(index); + } + } + }, + + _disabled: function(elem) { + return $(elem).hasClass( this.namespace + '-state-disabled' ); + }, + + + _disableOption: function(index) { + var optionElem = this._optionLis.eq(index); + if (optionElem) { + optionElem.addClass(this.namespace + '-state-disabled') + .find("a").attr("aria-disabled", true); + this.element.find("option").eq(index).attr("disabled", "disabled"); + } + }, + + _enableOption: function(index) { + var optionElem = this._optionLis.eq(index); + if (optionElem) { + optionElem.removeClass( this.namespace + '-state-disabled' ) + .find("a").attr("aria-disabled", false); + this.element.find("option").eq(index).removeAttr("disabled"); + } + }, + + _disableOptgroup: function(index) { + var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index ); + if (optGroupElem) { + optGroupElem.addClass(this.namespace + '-state-disabled') + .attr("aria-disabled", true); + this.element.find("optgroup").eq(index).attr("disabled", "disabled"); + } + }, + + _enableOptgroup: function(index) { + var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index ); + if (optGroupElem) { + optGroupElem.removeClass(this.namespace + '-state-disabled') + .attr("aria-disabled", false); + this.element.find("optgroup").eq(index).removeAttr("disabled"); + } + }, + + index: function(newValue) { + if (arguments.length) { + if (!this._disabled($(this._optionLis[newValue]))) { + this.element[0].selectedIndex = newValue; + this._refreshValue(); + } else { + return false; + } + } else { + return this._selectedIndex(); + } + }, + + value: function(newValue) { + if (arguments.length) { + this.element[0].value = newValue; + this._refreshValue(); + } else { + return this.element[0].value; + } + }, + + _refreshValue: function() { + var activeClass = (this.options.style == "popup") ? " ui-state-active" : ""; + var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000); + // deselect previous + this.list + .find('.' + this.widgetBaseClass + '-item-selected') + .removeClass(this.widgetBaseClass + "-item-selected" + activeClass) + .find('a') + .attr('aria-selected', 'false') + .attr('id', ''); + // select new + this._selectedOptionLi() + .addClass(this.widgetBaseClass + "-item-selected" + activeClass) + .find('a') + .attr('aria-selected', 'true') + .attr('id', activeID); + + // toggle any class brought in from option + var currentOptionClasses = (this.newelement.data('optionClasses') ? this.newelement.data('optionClasses') : ""); + var newOptionClasses = (this._selectedOptionLi().data('optionClasses') ? this._selectedOptionLi().data('optionClasses') : ""); + this.newelement + .removeClass(currentOptionClasses) + .data('optionClasses', newOptionClasses) + .addClass( newOptionClasses ) + .find('.' + this.widgetBaseClass + '-status') + .html( + this._selectedOptionLi() + .find('a:eq(0)') + .html() + ); + + this.list.attr('aria-activedescendant', activeID); + }, + + _refreshPosition: function() { + var o = this.options; + + // if its a native pop-up we need to calculate the position of the selected li + if ( o.style == "popup" && !o.positionOptions.offset ) { + var selected = this._selectedOptionLi(); + var _offset = "0 -" + ( selected.outerHeight() + selected.offset().top - this.list.offset().top ); + } + // update zIndex if jQuery UI is able to process + var zIndexElement = this.element.zIndex(); + if ( zIndexElement ) { + this.listWrap.css( 'zIndex', zIndexElement ); + } + this.listWrap.position({ + // set options for position plugin + of: o.positionOptions.of || this.newelement, + my: o.positionOptions.my, + at: o.positionOptions.at, + offset: o.positionOptions.offset || _offset, + collision: o.positionOptions.collision || 'flip' + }); + } +}); + +})(jQuery); diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee new file mode 100644 index 00000000000..761567942fc --- /dev/null +++ b/app/assets/javascripts/profile.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/projects.js b/app/assets/javascripts/projects.js new file mode 100644 index 00000000000..dc13d006077 --- /dev/null +++ b/app/assets/javascripts/projects.js @@ -0,0 +1,41 @@ +$(document).ready(function(){ + $('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live("click", function() { + history.pushState({ path: this.path }, '', this.href) + }) + + $("#tree-slider tr.tree-item").live('click', function(e){ + if(e.target.nodeName != "A") { + e.stopPropagation(); + $(this).find("td.tree-item-file-name a").click(); + return false; + } + }); + + $("#projects-list .project").live('click', function(e){ + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); + + $("#issues-table .issue").live('click', function(e){ + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); + + $(document).keypress(function(e) { + if( $(e.target).is(":input") ) return; + switch(e.which) { + case 115: focusSearch(); + e.preventDefault(); + } + }); +}); + +function focusSearch() { + $("#search").focus(); +} diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 00000000000..42fe63db2b3 --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,7 @@ +/* + * This is a manifest file that'll automatically include all the stylesheets available in this directory + * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at + * the top of the compiled file, but it's generally better to create a new file per style scope. + *= require_self + *= require_tree . +*/ diff --git a/app/assets/stylesheets/dashboard.css.scss b/app/assets/stylesheets/dashboard.css.scss new file mode 100644 index 00000000000..e8f34fdd057 --- /dev/null +++ b/app/assets/stylesheets/dashboard.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Dashboard controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/highlight.css.scss b/app/assets/stylesheets/highlight.css.scss new file mode 100644 index 00000000000..05cb98e73cb --- /dev/null +++ b/app/assets/stylesheets/highlight.css.scss @@ -0,0 +1,135 @@ +@mixin round-borders-all($radius) { + border: 1px solid #eaeaea; + -moz-border-radius: $radius; + -webkit-border-radius: $radius; + border-radius: $radius; +} + +table.highlighttable +{ + margin:0px; + padding:0px; + font-size:12px; + table-layout:fixed +} + +td.code, +td.linenos{ + padding:0; + margin:0; + vertical-align:top; +} + +.highlight{ + background:none; + padding:10px 0px 0px 0; + margin-left:10px; +} +.highlight pre{ +} + +.linenodiv pre { + white-space:pre-line; +} + +td.linenos { + background:#ECECEC; + color:#777; + padding:10px 0px 0px 10px; + float:left; + width:45px; + border-right: 1px solid #ccc; + +} + +td.code .highlight { + overflow-x: scroll; +} +table.highlighttable pre{ + padding:0; + margin:0; + font-family: 'Courier New', 'andale mono','lucida console',monospace; + color: #333; + text-align:left; +} + + +.git-empty .highlight { + @include round-borders-all(4px); + background:#eee; + padding:5px; + //overflow-x:scroll; + pre{ + padding:0; + line-height:2.0; + margin:0; + font-family: 'Courier New', 'andale mono','lucida console',monospace; + color: #333; + text-align:left;} + } + +.shadow{ + -webkit-box-shadow:0 5px 15px #000; + -moz-box-shadow:0 5px 15px #000; + box-shadow:0 5px 15px #000; +} + + +.hll { background-color: #ffffff } +.c { color: #888888; font-style: italic } /* Comment */ +.err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.k { color: #000000; font-weight: bold } /* Keyword */ +.cm { color: #888888 } /* Comment.Multiline */ +.cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ +.c1 { color: #888888 } /* Comment.Single */ +.cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ +.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #aa0000 } /* Generic.Error */ +.gh { color: #303030 } /* Generic.Heading */ +.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.go { color: #888888 } /* Generic.Output */ +.gp { color: #555555 } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #606060 } /* Generic.Subheading */ +.gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc{font-weight:bold;} /* Keyword.Constant */ +.highlight .kd{font-weight:bold;} /* Keyword.Declaration */ +.highlight .kn{font-weight:bold;} /* Keyword.Namespace */ +.highlight .kp{font-weight:bold;} /* Keyword.Pseudo */ +.highlight .kr{font-weight:bold;} /* Keyword.Reserved */ +.highlight .kt{color:#458;font-weight:bold;} /* Keyword.Type */ +.m { color: #0000DD; font-weight: bold } /* Literal.Number */ +.s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ +.highlight .na{color:#008080;} /* Name.Attribute */ +.highlight .nb{color:#0086B3;} /* Name.Builtin */ +.highlight .nc{color:#458;font-weight:bold;} /* Name.Class */ +.highlight .no{color:#008080;} /* Name.Constant */ +.highlight .ni{color:#800080;} +.highlight .ne{color:#900;font-weight:bold;} /* Name.Exception */ +.highlight .nf{color:#900;font-weight:bold;} /* Name.Function */ +.highlight .nn{color:#005;font-weight:bold;} /* Name.Namespace */ +.highlight .nt{color:#000080;} /* Name.Tag */ +.highlight .nv{color:#008080;} /* Name.Variable */ +.py { color: #336699; font-weight: bold } /* Name.Property */ +.ow { color: #008800 } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi {color:#099;} /* Literal.Number.Integer */ +.mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ +.sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ +.highlight .sc{color:#d14;} /* Literal.String.Char */ +.sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ +.highlight .s2{color:#d14;} /* Literal.String.Double */ +.highlight .se{color:#d14;} /* Literal.String.Escape */ +.highlight .sh{color:#d14;} /* Literal.String.Heredoc */ +.highlight .si{color:#d14;} /* Literal.String.Interpol */ +.highlight .sx{color:#d14;} /* Literal.String.Other */ +.highlight .sr{color:#d14;} /* Literal.String.Regex */ +.highlight .s1{color:#d14;} /* Literal.String.Single */ +.highlight .ss{color:#d14;} /* Literal.String.Symbol */ +.bp { color: #003388 } /* Name.Builtin.Pseudo */ +.vc { color: #336699 } /* Name.Variable.Class */ +.vg { color: #dd7700 } /* Name.Variable.Global */ +.vi { color: #3333bb } diff --git a/app/assets/stylesheets/issues.css.scss b/app/assets/stylesheets/issues.css.scss new file mode 100644 index 00000000000..1fbc8cb88df --- /dev/null +++ b/app/assets/stylesheets/issues.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Issues controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/jquery.ui.selectmenu.css b/app/assets/stylesheets/jquery.ui.selectmenu.css new file mode 100644 index 00000000000..37cfbd0557a --- /dev/null +++ b/app/assets/stylesheets/jquery.ui.selectmenu.css @@ -0,0 +1,33 @@ +/* Selectmenu +----------------------------------*/ +.ui-selectmenu { background:none; font-size:12px;display: block; display: inline-block; position: relative; height: 2.2em; vertical-align: middle; text-decoration: none; overflow: hidden; zoom: 1; } +.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; } +.ui-selectmenu-menu { padding:0; margin:0; position:absolute; top: 0; display: none; z-index: 1005;} /* z-index: 1005 to make selectmenu work with dialog */ +.ui-selectmenu-menu ul { padding:0; margin:0; list-style:none; position: relative; overflow: auto; overflow-y: auto ; overflow-x: hidden; } +.ui-selectmenu-open { display: block; } +.ui-selectmenu.ui-widget { background:none; } +.ui-selectmenu-menu-popup { margin-top: -1px; } +.ui-selectmenu-menu-dropdown { } +.ui-selectmenu-menu li.ui-state-active { background:#F7FBFC; border:none; padding:1px 0;} +.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; } +.ui-selectmenu-menu li a,.ui-selectmenu-status { line-height: 1.4em; display: block; padding: .405em 2.1em .405em 1em; outline:none; text-decoration:none; } +.ui-selectmenu-menu li.ui-state-disabled a, .ui-state-disabled { cursor: default; } +.ui-selectmenu-menu li.ui-selectmenu-hasIcon a, +.ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; } +.ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; } +.ui-selectmenu-status { line-height: 1.4em; } +.ui-selectmenu-open li.ui-selectmenu-item-focus { background: none repeat scroll 0 0 #FFF6BF; border:1px solid #eaeaea;} +.ui-selectmenu-open li.ui-selectmenu-item-selected { } +.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; } +.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; } +.ui-selectmenu-menu li .ui-selectmenu-item-content { } +.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; } +/* for optgroups */ +.ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; } +.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding: .6em .5em 0; font-weight: bold; } +.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; } +/* IE6 workaround (dotted transparent borders) */ +* html .ui-selectmenu-menu li { border-color: pink; filter:chroma(color=pink); width:100%; } +* html .ui-selectmenu-menu li a { position: relative } +/* IE7 workaround (opacity disabled) */ +*+html .ui-state-disabled, *+html .ui-state-disabled a { color: silver; } diff --git a/app/assets/stylesheets/profile.css.scss b/app/assets/stylesheets/profile.css.scss new file mode 100644 index 00000000000..22ee5087687 --- /dev/null +++ b/app/assets/stylesheets/profile.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Profile controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss new file mode 100644 index 00000000000..0ee40f512ac --- /dev/null +++ b/app/assets/stylesheets/projects.css.scss @@ -0,0 +1,541 @@ +// Place all the styles related to the Projects controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + +@mixin round-borders-bottom($radius) { + border-top: 1px solid #eaeaea; + + -moz-border-radius-bottomright: $radius; + -moz-border-radius-bottomleft: $radius; + + border-bottom-right-radius: $radius; + border-bottom-left-radius: $radius; + + -webkit-border-bottom-left-radius: $radius; + -webkit-border-bottom-right-radius: $radius; +} + +@mixin round-borders-top($radius) { + border-top: 1px solid #eaeaea; + + -moz-border-radius-topright: $radius; + -moz-border-radius-topleft: $radius; + + border-top-right-radius: $radius; + border-top-left-radius: $radius; + + -webkit-border-top-left-radius: $radius; + -webkit-border-top-right-radius: $radius; +} + +@mixin round-borders-all($radius) { + border: 1px solid #eaeaea; + -moz-border-radius: $radius; + -webkit-border-radius: $radius; + border-radius: $radius; +} + +@mixin hover-color { + background: #fff !important; + background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#FFF6BF)) !important; + background: -moz-linear-gradient(top,#fff,#FFF6BF) !important; + background: transparent 9 !important; +} + +.diff_file { + border:1px solid #CCC; + margin-bottom:1em; + + .diff_file_header { + padding:5px 5px; + border-bottom:1px solid #CCC; + background: #eee; + } + .diff_file_content { + overflow-x: scroll; + background:#fff; + color:#333; + font-size: 12px; + font-family: 'Courier New', 'andale mono','lucida console',monospace; + } + .diff_file_content_image { + background:#eee; + text-align:center; + img { + padding:100px; + max-width:300px; + } + } +} + +#logo { + &:hover { + background:none; + } +} + +.file_stats { + margin-bottom:10px; + @include round-borders-all(4px); + + span { + border-top: 1px solid #eaeaea; + padding:5px 5px; + display:block; + &:first-child { + border-top:none; + } + + img { + width:18px; + float:left; + margin-right: 6px; + } + } +} + +.round-borders { + @include round-borders-all(4px); + padding: 4px 0px; +} +table.round-borders { + float:left; +} + +.day-commits-table { + @include round-borders-all(4px); + padding: 4px 0px; + margin-bottom:10px; + display:block; + width:100%; + background: #E6F1F6; + + .day-header { + padding:10px; + h3 { + margin:0px; + } + } + + ul { + display:block; + list-style:none; + margin:0px; + padding:0px; + + li.commit { + display:list-item; + padding:8px; + margin:0px; + background: #F7FBFC; + border-top: 1px solid #E2EAEE; + + &:first-child { + border-top: 1px solid #E2EAEE; + } + &:nth-child(2n+1) { + background: white; + } + + a.button { + width:85px; + padding:10px; + margin:0px; + float:right; + } + p { + margin-bottom: 3px; + font-size: 13px; + } + } + } +} +@mixin panel-color { + background: #111 !important; + background: -webkit-gradient(linear,left top,left bottom,from(#333),to(#111)) !important; + background: -moz-linear-gradient(top,#333,#111) !important; + background: transparent 9 !important; +} + + +#header-panel { + @include panel-color; + height:40px; + position:fixed; + z-index:999; + top:0px; + width:100%; + margin-bottom:10px; + overflow:hidden; + .button{ + color:#bbb; + border:none; + margin:0px; + height:25px; + background:transparent; + padding:10px 20px 5px 20px; + &:hover{ + color:white; + } + + &.current { + border-bottom: 3px solid #EAEAEA !important; + padding: 10px 20px 0; + color: #eaeaea; + } + } + .search-holder { + float:left; + width:290px; + input { + @include round-borders-all(4px); + width:290px; + border-color:#888; + padding:5px; + background:#666; + color:#222; + &:focus { + background:#fff; + color:#000; + } + } + } +} + +#content-container{ + min-height:250px; + background: #fff; + @include round-borders-bottom(8px); + borders:2px solid #eaeaea; + border-top: none; + padding:20px; +} + +body { + background: #eaeaea; +} + +a { + color: #111; +} + +.diff_file_content{ + .old_line, .new_line { + background:#ECECEC; + color:#777; + width:15px; + float:left; + padding: 0px 10px; + border-right: 1px solid #ccc; + } +} + + +.view_file_content{ + .old_line, .new_line { + background:#ECECEC; + color:#777; + width:15px; + float:left; + padding: 0px 10px; + border-right: 1px solid #ccc; + } + .old_line{ + display:none; + } +} + +.view_file { + border:1px solid #CCC; + margin-bottom:1em; + + .view_file_header { + padding:5px 5px; + border-bottom:1px solid #CCC; + background: #eee; + } + .view_file_content { + background:#fff; + color:#514721; + font-size: 11px; + } + .view_file_content_image { + background:#eee; + text-align:center; + img { + padding:100px; + max-width:300px; + } + } +} + +.back_small.button{ + +} + +input.ssh_project_url { + padding:5px; + margin:0px; + float:right; + width:400px; + text-align:center; +} + + + +.day-commits-table li.commit { + cursor:pointer; + + &:hover { + @include hover-color; + } +} + +/* +#FFF6BF +#FFD324 +*/ +#tree-slider tr.tree-item { + cursor:pointer; + + &:hover { + @include hover-color; + td { + @include hover-color; + } + } +} +#projects-list .project { + height:50px; +} + +#projects-list .project, +#issues-table .issue{ + cursor:pointer; + + &:hover { + @include hover-color; + td { + @include hover-color; + } + } +} + +.clear { + clear: both; +} + +.top_project_menu { + a { + border-right: 1px solid #FFFFFF; + box-shadow: -1px 0 #DDDDDD inset; + color: #666; + display: block; + font-size: 16px; + text-decoration: none; + line-height: 20px; + padding: 11px 26px 12px 24px; + text-shadow: 0 1px 0 #FFFFFF; + float:left; + + &.current { + background-color: #FFFFFF; + color: #222222; + } + } +} + +.top_bar { + margin-top:50px; + background-color: #F4F4F4; + @include round-borders-top(8px); + box-shadow: 0 1px #FFFFFF inset, 0 -1px #DDDDDD inset; + height: 43px; + overflow: hidden; + width:990px; +} + +/** FORM INPUTS **/ + +.user_new, +.edit_user, +.new_project, +.edit_project { + input[type='text'], + input[type='email'], + input[type='password'], + textarea { + width:400px; + padding:8px; + font-size:14px; + @include round-borders-all(4px); + } +} + +.input_button { + //@include round-borders-all(4px); + padding:8px; + font-size:14px; + cursor:pointer; + background-color: #F5F5F5; + border-color: #EEEEEE #DEDEDE #DEDEDE #EEEEEE; + border-right: 1px solid #DEDEDE; + border-style: solid; + border-width: 1px; +} + +tbody tr:nth-child(2n) td, tbody tr.even td { + background: none repeat scroll 0 0 #F7FBFC; + border-top: 1px solid #E2EAEE; + border-bottom: 1px solid #E2EAEE; +} + +.top_menu_count { + background: none repeat scroll 0 0 #FFF6BF; + border-color: #FFD324; + color: #514721; + border: 1px solid #DDDDDD; + padding: 2px; + font-size:12px; + position:relative; + top:-14px; + left:10px; + border-top:none; +} + +#logo { + color: #EAEAEA; + font-family: monospace; + font-size: 26px; + padding: 4px; + text-decoration: none; + text-shadow: #555 1px 1px; +} + +/** FALSH **/ + +#flash_container { + height:40px; + position:fixed; + z-index:1009; + top:0px; + width:100%; + margin-bottom:10px; + overflow:hidden; + background:white; + cursor:pointer; + border-bottom:1px solid #777; + + h4 { + color:#444; + font-size:22px; + padding-top:5px; + } +} + +/** Buttons **/ + +.lbutton, +.lite_button { + display:block; + float:left; + margin: 0px 5px; + padding:5px 10px; + + font-family:"Helvetica Neue", Arial, Helvetica, sans-serif; + border:1px solid #D3D3D3; + background:white; + font-size:12px !important; + line-height:130%; + text-decoration:none; + font-weight:bold; + color:#565656; + cursor:pointer; + + &:hover { + border:1px solid #C2E1EF; + color: #0099FF; + } + + &.hm { + margin: 0px 0px; + } + + &.vm { + margin: 5px 0px; + } +} + +/** Notes **/ + +#notes-list { + display:block; + list-style:none; + margin:0px; + padding:0px; + + li { + display:list-item; + padding:8px; + margin:0px; + background: #F7FBFC; + border-top: 1px solid #E2EAEE; + + &:first-child { + border-top: none; + } + &:nth-child(2n+1) { + background: white; + } + p { + margin-bottom: 3px; + font-size: 12px; + } + } +} + +.notes_count { + background: none repeat scroll 0 0 #FFF6BF; + border-color: #FFD324; + color: #514721; + border: 2px solid #DDDDDD; + margin-bottom: 1em; + margin-top: 3px; + padding: 2px 5px; + position: relative; + right: 6px; + top: 6px; +} +.note_author { + float:left; + width:60px; +} +.note_content { + float:left; + width:750px; +} + +.issue_notes { + .note_content { + float:left; + width:400px; + } +} + +#user_projects_limit{ + width: 60px; +} + +.project_thumb { + margin:20px 0; + width: 250px; + float:left; + padding:20px; + text-align:center; + p, h4 { + text-align:left; + } + .lbutton { + float:left; + } +} diff --git a/app/controllers/admin/mailer_controller.rb b/app/controllers/admin/mailer_controller.rb new file mode 100644 index 00000000000..05ad267fad5 --- /dev/null +++ b/app/controllers/admin/mailer_controller.rb @@ -0,0 +1,44 @@ +class Admin::MailerController < ApplicationController + before_filter :authenticate_user! + before_filter :authenticate_admin! + + def preview + + end + + def preview_note + @note = Note.first + @user = @note.author + @project = @note.project + case params[:type] + when "Commit" then + @commit = @project.commit + render :file => 'notify/note_commit_email.html.haml', :layout => 'notify' + when "Issue" then + @issue = Issue.first + render :file => 'notify/note_issue_email.html.haml', :layout => 'notify' + else + render :file => 'notify/note_wall_email.html.haml', :layout => 'notify' + end + rescue + render :text => "Preview not avaialble" + end + + def preview_user_new + @user = User.first + @password = "DHasJKDHAS!" + + render :file => 'notify/new_user_email.html.haml', :layout => 'notify' + rescue + render :text => "Preview not avaialble" + end + + def preview_issue_new + @issue = Issue.first + @user = @issue.assignee + @project = @issue.project + render :file => 'notify/new_issue_email.html.haml', :layout => 'notify' + rescue + render :text => "Preview not avaialble" + end +end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb new file mode 100644 index 00000000000..5575261959a --- /dev/null +++ b/app/controllers/admin/projects_controller.rb @@ -0,0 +1,74 @@ +class Admin::ProjectsController < ApplicationController + before_filter :authenticate_user! + before_filter :authenticate_admin! + + def index + @admin_projects = Project.page(params[:page]) + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @admin_projects } + end + end + + def show + @admin_project = Project.find_by_code(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @admin_project } + end + end + + def new + @admin_project = Project.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @admin_project } + end + end + + def edit + @admin_project = Project.find_by_code(params[:id]) + end + + def create + @admin_project = Project.new(params[:project]) + @admin_project.owner = current_user + + respond_to do |format| + if @admin_project.save + format.html { redirect_to [:admin, @admin_project], notice: 'Project was successfully created.' } + format.json { render json: @admin_project, status: :created, location: @admin_project } + else + format.html { render action: "new" } + format.json { render json: @admin_project.errors, status: :unprocessable_entity } + end + end + end + + def update + @admin_project = Project.find_by_code(params[:id]) + + respond_to do |format| + if @admin_project.update_attributes(params[:project]) + format.html { redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.' } + format.json { head :ok } + else + format.html { render action: "edit" } + format.json { render json: @admin_project.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @admin_project = Project.find_by_code(params[:id]) + @admin_project.destroy + + respond_to do |format| + format.html { redirect_to admin_projects_url } + format.json { head :ok } + end + end +end diff --git a/app/controllers/admin/team_members_controller.rb b/app/controllers/admin/team_members_controller.rb new file mode 100644 index 00000000000..bca9bfebbfd --- /dev/null +++ b/app/controllers/admin/team_members_controller.rb @@ -0,0 +1,75 @@ +class Admin::TeamMembersController < ApplicationController + before_filter :authenticate_user! + before_filter :authenticate_admin! + + def index + @admin_team_members = UsersProject.page(params[:page]).per(100).order("project_id DESC") + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @admin_team_members } + end + end + + def show + @admin_team_member = UsersProject.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @admin_team_member } + end + end + + def new + @admin_team_member = UsersProject.new(params[:team_member]) + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @admin_team_member } + end + end + + def edit + @admin_team_member = UsersProject.find(params[:id]) + end + + def create + @admin_team_member = UsersProject.new(params[:team_member]) + @admin_team_member.project_id = params[:team_member][:project_id] + + respond_to do |format| + if @admin_team_member.save + format.html { redirect_to admin_team_member_path(@admin_team_member), notice: 'UsersProject was successfully created.' } + format.json { render json: @admin_team_member, status: :created, location: @team_member } + else + format.html { render action: "new" } + format.json { render json: @admin_team_member.errors, status: :unprocessable_entity } + end + end + end + + def update + @admin_team_member = UsersProject.find(params[:id]) + @admin_team_member.project_id = params[:team_member][:project_id] + + respond_to do |format| + if @admin_team_member.update_attributes(params[:team_member]) + format.html { redirect_to admin_team_member_path(@admin_team_member), notice: 'UsersProject was successfully updated.' } + format.json { head :ok } + else + format.html { render action: "edit" } + format.json { render json: @admin_team_member.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @admin_team_member = UsersProject.find(params[:id]) + @admin_team_member.destroy + + respond_to do |format| + format.html { redirect_to admin_team_members_url } + format.json { head :ok } + end + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb new file mode 100644 index 00000000000..e9ad1e55983 --- /dev/null +++ b/app/controllers/admin/users_controller.rb @@ -0,0 +1,84 @@ +class Admin::UsersController < ApplicationController + before_filter :authenticate_user! + before_filter :authenticate_admin! + + def index + @admin_users = User.page(params[:page]) + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @admin_users } + end + end + + def show + @admin_user = User.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @admin_user } + end + end + + def new + @admin_user = User.new(:projects_limit => 10) + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @admin_user } + end + end + + def edit + @admin_user = User.find(params[:id]) + end + + def create + admin = params[:user].delete("admin") + + @admin_user = User.new(params[:user]) + @admin_user.admin = (admin && admin.to_i > 0) + + respond_to do |format| + if @admin_user.save + Notify.new_user_email(@admin_user, params[:user][:password]).deliver + format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully created.' } + format.json { render json: @admin_user, status: :created, location: @admin_user } + else + format.html { render action: "new" } + format.json { render json: @admin_user.errors, status: :unprocessable_entity } + end + end + end + + def update + admin = params[:user].delete("admin") + if params[:user][:password].empty? + params[:user].delete(:password) + params[:user].delete(:password_confirmation) + end + + @admin_user = User.find(params[:id]) + @admin_user.admin = (admin && admin.to_i > 0) + + respond_to do |format| + if @admin_user.update_attributes(params[:user]) + format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } + format.json { head :ok } + else + format.html { render action: "edit" } + format.json { render json: @admin_user.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @admin_user = User.find(params[:id]) + @admin_user.destroy + + respond_to do |format| + format.html { redirect_to admin_users_url } + format.json { head :ok } + end + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 00000000000..829126fee05 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,44 @@ +class ApplicationController < ActionController::Base + before_filter :authenticate_user! + protect_from_forgery + + helper_method :abilities, :can? + + rescue_from Gitosis::AccessDenied do |exception| + render :file => File.join(Rails.root, "public", "gitosis_error"), :layout => false + end + + protected + + def abilities + @abilities ||= Six.new + end + + def can?(object, action, subject) + abilities.allowed?(object, action, subject) + end + + def project + @project ||= Project.find_by_code(params[:project_id]) + end + + def add_project_abilities + abilities << Ability + end + + def authenticate_admin! + return redirect_to(new_user_session_path) unless current_user.is_admin? + end + + def authorize_project!(action) + return redirect_to(new_user_session_path) unless can?(current_user, action, project) + end + + def method_missing(method_sym, *arguments, &block) + if method_sym.to_s =~ /^authorize_(.*)!$/ + authorize_project!($1.to_sym) + else + super + end + end +end diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb new file mode 100644 index 00000000000..d7daec13f0f --- /dev/null +++ b/app/controllers/commits_controller.rb @@ -0,0 +1,44 @@ +require "base64" + +class CommitsController < ApplicationController + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_project! + + def index + @repo = project.repo + @branch = if !params[:branch].blank? + params[:branch] + elsif !params[:tag].blank? + params[:tag] + else + "master" + end + + if params[:path] + @commits = @repo.log(@branch, params[:path], :max_count => params[:limit] || 100, :skip => params[:offset] || 0) + else + @commits = @repo.commits(@branch, params[:limit] || 100, params[:offset] || 0) + end + + respond_to do |format| + format.html # index.html.erb + format.js + format.json { render json: @commits } + end + end + + def show + @commit = project.repo.commits(params[:id]).first + @notes = project.notes.where(:noteable_id => @commit.id, :noteable_type => "Commit") + @note = @project.notes.new(:noteable_id => @commit.id, :noteable_type => "Commit") + + respond_to do |format| + format.html # show.html.erb + format.js + format.json { render json: @commit } + end + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb new file mode 100644 index 00000000000..2476b17ef90 --- /dev/null +++ b/app/controllers/dashboard_controller.rb @@ -0,0 +1,5 @@ +class DashboardController < ApplicationController + def index + @projects = current_user.projects.all + end +end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb new file mode 100644 index 00000000000..c37f10a39f3 --- /dev/null +++ b/app/controllers/errors_controller.rb @@ -0,0 +1,5 @@ +class ErrorsController < ApplicationController + def gitosis + render :file => File.join(Rails.root, "public", "gitosis_error"), :layout => false + end +end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb new file mode 100644 index 00000000000..f8b4719809b --- /dev/null +++ b/app/controllers/issues_controller.rb @@ -0,0 +1,72 @@ +class IssuesController < ApplicationController + before_filter :authenticate_user! + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_issue! + before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update] + before_filter :authorize_admin_issue!, :only => [:destroy] + + respond_to :js + + def index + @issues = case params[:f].to_i + when 1 then @project.issues.all + when 2 then @project.issues.closed + when 3 then @project.issues.opened.assigned(current_user) + else @project.issues.opened + end + + respond_to do |format| + format.html # index.html.erb + format.js + end + end + + def new + @issue = @project.issues.new + respond_with(@issue) + end + + def edit + @issue = @project.issues.find(params[:id]) + respond_with(@issue) + end + + def show + @issue = @project.issues.find(params[:id]) + @notes = @issue.notes + @note = @project.notes.new(:noteable => @issue) + end + + def create + @issue = @project.issues.new(params[:issue]) + @issue.author = current_user + if @issue.save + Notify.new_issue_email(@issue).deliver + end + + respond_with(@issue) + end + + def update + @issue = @project.issues.find(params[:id]) + @issue.update_attributes(params[:issue]) + + respond_to do |format| + format.js + format.html { redirect_to [@project, @issue]} + end + end + + + def destroy + @issue = @project.issues.find(params[:id]) + @issue.destroy + + respond_to do |format| + format.js { render :nothing => true } + end + end +end diff --git a/app/controllers/keys_controller.rb b/app/controllers/keys_controller.rb new file mode 100644 index 00000000000..003de6b301a --- /dev/null +++ b/app/controllers/keys_controller.rb @@ -0,0 +1,38 @@ +class KeysController < ApplicationController + respond_to :js + + def index + @keys = current_user.keys.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @keys } + end + end + + def new + @key = current_user.keys.new + + respond_with(@key) + end + + def create + @key = current_user.keys.new(params[:key]) + @key.save + + respond_with(@key) + end + + # DELETE /keys/1 + # DELETE /keys/1.json + def destroy + @key = current_user.keys.find(params[:id]) + @key.destroy + + respond_to do |format| + format.html { redirect_to keys_url } + format.js { render :nothing => true } + format.json { head :ok } + end + end +end diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb new file mode 100644 index 00000000000..d0a40eb18e4 --- /dev/null +++ b/app/controllers/notes_controller.rb @@ -0,0 +1,49 @@ +class NotesController < ApplicationController + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_write_note!, :only => [:create] + before_filter :authorize_admin_note!, :only => [:destroy] + + respond_to :js + + def create + @note = @project.notes.new(params[:note]) + @note.author = current_user + + if @note.save + notify if params[:notify] == '1' + end + + + respond_to do |format| + format.html {redirect_to :back} + format.js + end + end + + def destroy + @note = @project.notes.find(params[:id]) + @note.destroy + + respond_to do |format| + format.js { render :nothing => true } + end + end + + protected + + def notify + @project.users.reject { |u| u.id == current_user.id } .each do |u| + case @note.noteable_type + when "Commit" then + Notify.note_commit_email(u, @note).deliver + when "Issue" then + Notify.note_issue_email(u, @note).deliver + else + Notify.note_wall_email(u, @note).deliver + end + end + end +end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb new file mode 100644 index 00000000000..666c6309dce --- /dev/null +++ b/app/controllers/profile_controller.rb @@ -0,0 +1,21 @@ +class ProfileController < ApplicationController + def show + @user = current_user + end + + def password + @user = current_user + end + + def password_update + params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} + @user = current_user + + if @user.update_attributes(params[:user]) + flash[:notice] = "Password was successfully updated. Please login with it" + redirect_to new_user_session_path + else + render :action => "password" + end + end +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb new file mode 100644 index 00000000000..080a6ee62f6 --- /dev/null +++ b/app/controllers/projects_controller.rb @@ -0,0 +1,152 @@ +class ProjectsController < ApplicationController + before_filter :project, :except => [:index, :new, :create] + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_project!, :except => [:index, :new, :create] + before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy] + + def index + @projects = current_user.projects.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @projects } + end + end + + def show + @repo = project.repo + @commit = @repo.commits.first + @tree = @commit.tree + @tree = @tree / params[:path] if params[:path] + + respond_to do |format| + format.html # show.html.erb + format.json { render json: project } + end + rescue Grit::NoSuchPathError => ex + respond_to do |format| + format.html {render "projects/empty"} + end + end + + def tree + @repo = project.repo + @branch = if !params[:branch].blank? + params[:branch] + elsif !params[:tag].blank? + params[:tag] + else + "master" + end + + if params[:commit_id] + @commit = @repo.commits(params[:commit_id]).first + else + @commit = @repo.commits(@branch || "master").first + end + @tree = @commit.tree + @tree = @tree / params[:path] if params[:path] + + respond_to do |format| + format.html # show.html.erb + format.js do + # temp solution + response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" + end + format.json { render json: project } + end + end + + def blob + @repo = project.repo + @commit = project.commit(params[:commit_id]) + @tree = project.tree(@commit, params[:path]) + + if @tree.is_a?(Grit::Blob) + send_data(@tree.data, :type => @tree.mime_type, :disposition => 'inline', :filename => @tree.name) + else + head(404) + end + end + + def new + @project = Project.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @project } + end + end + + def edit + end + + def create + @project = Project.new(params[:project]) + @project.owner = current_user + + Project.transaction do + @project.save! + @project.users_projects.create!(:admin => true, :read => true, :write => true, :user => current_user) + end + + respond_to do |format| + if @project.valid? + format.html { redirect_to @project, notice: 'Project was successfully created.' } + format.js + format.json { render json: @project, status: :created, location: @project } + else + format.html { render action: "new" } + format.js + format.json { render json: @project.errors, status: :unprocessable_entity } + end + end + rescue Gitosis::AccessDenied + render :js => "location.href = '#{errors_gitosis_path}'" and return + rescue StandardError => ex + @project.errors.add(:base, "Cant save project. Please try again later") + respond_to do |format| + format.html { render action: "new" } + format.js + format.json { render json: @project.errors, status: :unprocessable_entity } + end + end + + def update + respond_to do |format| + if project.update_attributes(params[:project]) + format.html { redirect_to project, notice: 'Project was successfully updated.' } + format.js + format.json { head :ok } + else + format.html { render action: "edit" } + format.js + format.json { render json: project.errors, status: :unprocessable_entity } + end + end + end + + def destroy + project.destroy + + respond_to do |format| + format.html { redirect_to projects_url } + format.json { head :ok } + end + end + + def wall + @notes = @project.common_notes + @note = Note.new + end + + protected + + def project + @project ||= Project.find_by_code(params[:id]) + end +end diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb new file mode 100644 index 00000000000..fd3c944b289 --- /dev/null +++ b/app/controllers/team_members_controller.rb @@ -0,0 +1,66 @@ +class TeamMembersController < ApplicationController + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_team_member! + before_filter :authorize_admin_team_member!, :only => [:new, :create, :destroy, :update] + + def show + @team_member = project.users_projects.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.js + format.json { render json: @team_member } + end + end + + def new + @team_member = project.users_projects.new + + respond_to do |format| + format.html # new.html.erb + format.js + format.json { render json: @team_member } + end + end + + def create + @team_member = UsersProject.new(params[:team_member]) + @team_member.project = project + + respond_to do |format| + if @team_member.save + format.html { redirect_to @team_member, notice: 'Team member was successfully created.' } + format.js + format.json { render json: @team_member, status: :created, location: @team_member } + else + format.html { render action: "new" } + format.js + format.json { render json: @team_member.errors, status: :unprocessable_entity } + end + end + end + + def update + @team_member = project.users_projects.find(params[:id]) + @team_member.update_attributes(params[:team_member]) + + respond_to do |format| + format.js + format.html { redirect_to team_project_path(@project)} + end + end + + def destroy + @team_member = project.users_projects.find(params[:id]) + @team_member.destroy + + respond_to do |format| + format.html { redirect_to root_path } + format.json { head :ok } + format.js { render :nothing => true } + end + end +end diff --git a/app/helpers/admin/projects_helper.rb b/app/helpers/admin/projects_helper.rb new file mode 100644 index 00000000000..348fb1f1a7b --- /dev/null +++ b/app/helpers/admin/projects_helper.rb @@ -0,0 +1,2 @@ +module Admin::ProjectsHelper +end diff --git a/app/helpers/admin/users_helper.rb b/app/helpers/admin/users_helper.rb new file mode 100644 index 00000000000..5995c2aa82d --- /dev/null +++ b/app/helpers/admin/users_helper.rb @@ -0,0 +1,2 @@ +module Admin::UsersHelper +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 00000000000..89a906f0472 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,77 @@ +require 'digest/md5' +module ApplicationHelper + def gravatar_icon(user_email) + "http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon" + end + + def commit_name(project, commit) + if project.commit.id == commit.id + "master" + else + commit.id + end + end + + def admin_namespace? + controller.class.name.split("::").first=="Admin" + end + + def projects_namespace? + !current_page?(root_url) && + controller.controller_name != "keys" && + !admin_namespace? + end + + def last_commit(project) + if project.repo_exists? + time_ago_in_words(project.commit.committed_date) + " ago" + else + "Never" + end + end + + def search_autocomplete_source + projects = current_user.projects.map{ |p| { :label => p.name, :url => project_path(p) } } + default_nav = [ + { :label => "Keys", :url => keys_path }, + { :label => "Projects", :url => projects_path }, + { :label => "Admin", :url => admin_root_path } + ] + + project_nav = [] + + if @project && !@project.new_record? + project_nav = [ + { :label => "#{@project.code} / Issues", :url => project_issues_path(@project) }, + { :label => "#{@project.code} / Wall", :url => wall_project_path(@project) }, + { :label => "#{@project.code} / Tree", :url => tree_project_path(@project) }, + { :label => "#{@project.code} / Commits", :url => project_commits_path(@project) }, + { :label => "#{@project.code} / Team", :url => team_project_path(@project) } + ] + end + + [projects, default_nav, project_nav].flatten.to_json + end + + def handle_file_type(file_name, mime_type) + if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ + :ruby + elsif file_name =~ /\.py$/ + :python + elsif file_name =~ /(\.pl|\.scala|\.c|\.cpp|\.java|\.haml|\.html|\.sass|\.scss|\.xml|\.php|\.erb)$/ + $1[1..-1].to_sym + elsif file_name =~ /\.js$/ + :javascript + elsif file_name =~ /\.sh$/ + :bash + elsif file_name =~ /\.coffee$/ + :coffeescript + elsif file_name =~ /\.yml$/ + :yaml + elsif file_name =~ /\.md$/ + :minid + else + :text + end + end +end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb new file mode 100644 index 00000000000..f1b546684f0 --- /dev/null +++ b/app/helpers/commits_helper.rb @@ -0,0 +1,24 @@ +module CommitsHelper + def diff_line(line, line_new = 0, line_old = 0) + full_line = html_escape(line.gsub(/\n/, '')) + color = if line[0] == "+" + full_line = " #{line_new} " + full_line + "#DFD" + elsif line[0] == "-" + full_line = "#{line_old}  " + full_line + "#FDD" + else + full_line = "#{line_old}#{line_new} " + full_line + "none" + end + + raw "
      #{full_line}
      " + end + + def more_commits_link + offset = params[:offset] || 0 + limit = params[:limit] || 100 + link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit), + :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link" + end +end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb new file mode 100644 index 00000000000..a94ddfc2e33 --- /dev/null +++ b/app/helpers/dashboard_helper.rb @@ -0,0 +1,2 @@ +module DashboardHelper +end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb new file mode 100644 index 00000000000..bfb9d25e564 --- /dev/null +++ b/app/helpers/issues_helper.rb @@ -0,0 +1,2 @@ +module IssuesHelper +end diff --git a/app/helpers/keys_helper.rb b/app/helpers/keys_helper.rb new file mode 100644 index 00000000000..d1a77931a92 --- /dev/null +++ b/app/helpers/keys_helper.rb @@ -0,0 +1,2 @@ +module KeysHelper +end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb new file mode 100644 index 00000000000..5a0d6b31f8c --- /dev/null +++ b/app/helpers/profile_helper.rb @@ -0,0 +1,2 @@ +module ProfileHelper +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb new file mode 100644 index 00000000000..d570dff4639 --- /dev/null +++ b/app/helpers/projects_helper.rb @@ -0,0 +1,6 @@ +module ProjectsHelper + def view_mode_style(type) + cookies["project_view"] ||= "tile" + cookies["project_view"] == type ? nil : "display:none" + end +end diff --git a/app/helpers/team_members_helper.rb b/app/helpers/team_members_helper.rb new file mode 100644 index 00000000000..6b8a38f84fe --- /dev/null +++ b/app/helpers/team_members_helper.rb @@ -0,0 +1,2 @@ +module TeamMembersHelper +end diff --git a/app/mailers/.gitkeep b/app/mailers/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb new file mode 100644 index 00000000000..56e4de9b392 --- /dev/null +++ b/app/mailers/notify.rb @@ -0,0 +1,41 @@ +class Notify < ActionMailer::Base + default_url_options[:host] = "gitlabhq.com" + default from: "notify@gitlabhq.com" + + def new_user_email(user, password) + @user = user + @password = password + mail(:to => @user.email, :subject => "gitlab | Account was created for you") + end + + def new_issue_email(issue) + @user = issue.assignee + @project = issue.project + @issue = issue + + mail(:to => @user.email, :subject => "gitlab | New Issue was created") + end + + def note_wall_email(user, note) + @user = user + @note = note + @project = note.project + mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ") + end + + def note_commit_email(user, note) + @user = user + @note = note + @project = note.project + @commit = @project.repo.commits(note.noteable_id).first + mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ") + end + + def note_issue_email(user, note) + @user = user + @note = note + @project = note.project + @issue = note.noteable + mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ") + end +end diff --git a/app/models/.gitkeep b/app/models/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 00000000000..0a2c45f1289 --- /dev/null +++ b/app/models/ability.rb @@ -0,0 +1,34 @@ +class Ability + def self.allowed(object, subject) + case subject.class.name + when "Project" then project_abilities(object, subject) + else [] + end + end + + def self.project_abilities(user, project) + rules = [] + + rules << [ + :read_project, + :read_issue, + :read_team_member, + :read_note + ] if project.readers.include?(user) + + rules << [ + :write_project, + :write_issue, + :write_note + ] if project.writers.include?(user) + + rules << [ + :admin_project, + :admin_issue, + :admin_team_member, + :admin_note + ] if project.admins.include?(user) + + rules.flatten + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb new file mode 100644 index 00000000000..0399607efe6 --- /dev/null +++ b/app/models/issue.rb @@ -0,0 +1,39 @@ +class Issue < ActiveRecord::Base + belongs_to :project + belongs_to :author, :class_name => "User" + belongs_to :assignee, :class_name => "User" + has_many :notes, :as => :noteable + + attr_protected :author, :author_id, :project, :project_id + + validates_presence_of :project_id + validates_presence_of :assignee_id + validates_presence_of :author_id + + validates :title, + :presence => true, + :length => { :within => 0..255 } + + validates :content, + :presence => true, + :length => { :within => 0..2000 } + + scope :opened, where(:closed => false) + scope :closed, where(:closed => true) + scope :assigned, lambda { |u| where(:assignee_id => u.id)} +end +# == Schema Information +# +# Table name: issues +# +# id :integer not null, primary key +# title :string(255) +# content :text +# assignee_id :integer +# author_id :integer +# project_id :integer +# created_at :datetime +# updated_at :datetime +# closed :boolean default(FALSE), not null +# + diff --git a/app/models/key.rb b/app/models/key.rb new file mode 100644 index 00000000000..9fa8958772d --- /dev/null +++ b/app/models/key.rb @@ -0,0 +1,58 @@ +class Key < ActiveRecord::Base + belongs_to :user + + validates :title, + :presence => true, + :length => { :within => 0..255 } + + validates :key, + :presence => true, + :uniqueness => true, + :length => { :within => 0..555 } + + before_save :set_identifier + after_save :update_gitosis + after_destroy :gitosis_delete_key + + def set_identifier + self.identifier = "#{user.identifier}_#{Time.now.to_i}" + end + + def update_gitosis + Gitosis.new.configure do |c| + c.update_keys(identifier, key) + + projects.each do |project| + c.update_project(project.path, project.gitosis_writers) + end + end + end + + def gitosis_delete_key + Gitosis.new.configure do |c| + c.delete_key(identifier) + + projects.each do |project| + c.update_project(project.path, project.gitosis_writers) + end + end + end + + #projects that has this key + def projects + user.projects + end +end +# == Schema Information +# +# Table name: keys +# +# id :integer not null, primary key +# user_id :integer not null +# created_at :datetime +# updated_at :datetime +# key :text +# title :string(255) +# identifier :string(255) +# + diff --git a/app/models/note.rb b/app/models/note.rb new file mode 100644 index 00000000000..f89fb9f8995 --- /dev/null +++ b/app/models/note.rb @@ -0,0 +1,41 @@ +require 'carrierwave/orm/activerecord' +require 'file_size_validator' + +class Note < ActiveRecord::Base + belongs_to :project + belongs_to :noteable, :polymorphic => true + belongs_to :author, + :class_name => "User" + + attr_protected :author, :author_id + + validates_presence_of :project + + validates :note, + :presence => true, + :length => { :within => 0..255 } + + validates :attachment, + :file_size => { + :maximum => 10.megabytes.to_i + } + + scope :common, where(:noteable_id => nil) + + mount_uploader :attachment, AttachmentUploader +end +# == Schema Information +# +# Table name: notes +# +# id :integer not null, primary key +# note :string(255) +# noteable_id :string(255) +# noteable_type :string(255) +# author_id :integer +# created_at :datetime +# updated_at :datetime +# project_id :integer +# attachment :string(255) +# + diff --git a/app/models/project.rb b/app/models/project.rb new file mode 100644 index 00000000000..17b556c3471 --- /dev/null +++ b/app/models/project.rb @@ -0,0 +1,164 @@ +require "grit" + +class Project < ActiveRecord::Base + belongs_to :owner, :class_name => "User" + + has_many :issues, :dependent => :destroy + has_many :users_projects, :dependent => :destroy + has_many :users, :through => :users_projects + has_many :notes, :dependent => :destroy + + validates :name, + :uniqueness => true, + :presence => true, + :length => { :within => 0..255 } + + validates :path, + :uniqueness => true, + :presence => true, + :length => { :within => 0..255 } + + validates :description, + :length => { :within => 0..2000 } + + validates :code, + :presence => true, + :uniqueness => true, + :length => { :within => 3..12 } + + validates :owner, + :presence => true + + validate :check_limit + + before_save :format_code + after_destroy :destroy_gitosis_project + after_save :update_gitosis_project + + attr_protected :private_flag, :owner_id + + scope :public_only, where(:private_flag => false) + + def to_param + code + end + + def common_notes + notes.where(:noteable_type => ["", nil]) + end + + def format_code + read_attribute(:code).downcase.strip.gsub(' ', '') + end + + def update_gitosis_project + Gitosis.new.configure do |c| + c.update_project(path, gitosis_writers) + end + end + + def destroy_gitosis_project + Gitosis.new.configure do |c| + c.destroy_project(self) + end + end + + def add_access(user, *access) + opts = { :user => user } + access.each { |name| opts.merge!(name => true) } + users_projects.create(opts) + end + + def reset_access(user) + users_projects.where(:project_id => self.id, :user_id => user.id).destroy if self.id + end + + def writers + @writers ||= users_projects.includes(:user).where(:write => true).map(&:user) + end + + def gitosis_writers + keys = Key.joins({:user => :users_projects}).where("users_projects.project_id = ? AND users_projects.write = ?", id, true) + keys.map(&:identifier) + end + + def readers + @readers ||= users_projects.includes(:user).where(:read => true).map(&:user) + end + + def admins + @admins ||=users_projects.includes(:user).where(:admin => true).map(&:user) + end + + def public? + !private_flag + end + + def private? + private_flag + end + + def url_to_repo + "#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git" + end + + def path_to_repo + GITOSIS["base_path"] + path + ".git" + end + + def repo + @repo ||= Grit::Repo.new(path_to_repo) + end + + def tags + repo.tags.map(&:name).sort.reverse + end + + def repo_exists? + repo rescue false + end + + def commit(commit_id = nil) + if commit_id + repo.commits(commit_id).first + else + repo.commits.first + end + end + + def tree(fcommit, path = nil) + fcommit = commit if fcommit == :head + tree = fcommit.tree + path ? (tree / path) : tree + end + + def check_limit + unless owner.can_create_project? + errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") + end + rescue + errors[:base] << ("Cant check your ability to create project") + end + + def valid_repo? + repo + rescue + errors.add(:path, "Invalid repository path") + false + end +end +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# description :text +# created_at :datetime +# updated_at :datetime +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer +# + diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 00000000000..0972f006dbd --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,62 @@ +class User < ActiveRecord::Base + # Include default devise modules. Others available are: + # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, + :recoverable, :rememberable, :trackable, :validatable + + # Setup accessible (or protected) attributes for your model + attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :projects_limit + + has_many :users_projects, :dependent => :destroy + has_many :projects, :through => :users_projects + has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id + has_many :keys, :dependent => :destroy + has_many :issues, + :foreign_key => :author_id, + :dependent => :destroy + + has_many :assigned_issues, + :class_name => "Issue", + :foreign_key => :assignee_id, + :dependent => :destroy + + scope :not_in_project, lambda { |project| where("id not in (:ids)", :ids => project.users.map(&:id) ) } + + def identifier + email.gsub "@", "_" + end + + def is_admin? + admin + end + + def can_create_project? + projects_limit >= my_own_projects.count + end + + def last_activity_project + projects.first + end +end +# == Schema Information +# +# Table name: users +# +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(128) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer +# + diff --git a/app/models/users_project.rb b/app/models/users_project.rb new file mode 100644 index 00000000000..bdc10633a3e --- /dev/null +++ b/app/models/users_project.rb @@ -0,0 +1,35 @@ +class UsersProject < ActiveRecord::Base + belongs_to :user + belongs_to :project + + attr_protected :project_id, :project + + after_commit :update_gitosis_project + + validates_uniqueness_of :user_id, :scope => [:project_id] + validates_presence_of :user_id + validates_presence_of :project_id + + delegate :name, :email, :to => :user, :prefix => true + + def update_gitosis_project + Gitosis.new.configure do |c| + c.update_project(project.path, project.gitosis_writers) + end + end + +end +# == Schema Information +# +# Table name: users_projects +# +# id :integer not null, primary key +# user_id :integer not null +# project_id :integer not null +# read :boolean default(FALSE) +# write :boolean default(FALSE) +# admin :boolean default(FALSE) +# created_at :datetime +# updated_at :datetime +# + diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb new file mode 100644 index 00000000000..4ba19ace8d1 --- /dev/null +++ b/app/uploaders/attachment_uploader.rb @@ -0,0 +1,49 @@ +# encoding: utf-8 + +class AttachmentUploader < CarrierWave::Uploader::Base + + # Include RMagick or ImageScience support: + # include CarrierWave::RMagick + # include CarrierWave::MiniMagick + # include CarrierWave::ImageScience + + # Choose what kind of storage to use for this uploader: + storage :file + # storage :fog + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + # version :thumb do + # process :scale => [50, 50] + # end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + # def extension_white_list + # %w(jpg jpeg gif png) + # end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + +end diff --git a/app/views/admin/_top_menu.html.haml b/app/views/admin/_top_menu.html.haml new file mode 100644 index 00000000000..d63caa7e8ee --- /dev/null +++ b/app/views/admin/_top_menu.html.haml @@ -0,0 +1,6 @@ +%div.top_project_menu + %span= link_to "Users", admin_users_path, :style => "width:50px;", :class => controller.controller_name == "users" ? "current" : nil + %span= link_to "Projects", admin_projects_path, :style => "width:50px;", :class => controller.controller_name == "projects" ? "current" : nil + %span= link_to "Teams", admin_team_members_path, :style => "width:50px;", :class => controller.controller_name == "team_members" ? "current" : nil + %span= link_to "Emails", admin_emails_path, :style => "width:50px;", :class => controller.controller_name == "mailer" ? "current" : nil + diff --git a/app/views/admin/mailer/preview.html.haml b/app/views/admin/mailer/preview.html.haml new file mode 100644 index 00000000000..7b723cb8021 --- /dev/null +++ b/app/views/admin/mailer/preview.html.haml @@ -0,0 +1,29 @@ +%p This is page with preview for all system emails that are sent to user +%p Email previews built based on existing Project/Commit/Issue base - so some preview maybe unavailable unless object appear in system + +#accordion + %h3 + %a New user + %div + %iframe{ :src=> admin_mailer_preview_user_new_path, :width=>"100%", :height=>"350"} + %h3 + %a New issue + %div + %iframe{ :src=> admin_mailer_preview_issue_new_path, :width=>"100%", :height=>"350"} + %h3 + %a Commit note + %div + %iframe{ :src=> admin_mailer_preview_note_path(:type => "Commit"), :width=>"100%", :height=>"350"} + %h3 + %a Issue note + %div + %iframe{ :src=> admin_mailer_preview_note_path(:type => "Issue"), :width=>"100%", :height=>"350"} + %h3 + %a Wall note + %div + %iframe{ :src=> admin_mailer_preview_note_path(:type => "Wall"), :width=>"100%", :height=>"350"} + + +:javascript + $(function() { + $( "#accordion" ).accordion(); }); diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml new file mode 100644 index 00000000000..9823e594e1a --- /dev/null +++ b/app/views/admin/projects/_form.html.haml @@ -0,0 +1,30 @@ += form_for [:admin, @admin_project] do |f| + -if @admin_project.errors.any? + #error_explanation + %h2= "#{pluralize(@admin_project.errors.count, "error")} prohibited this admin_project from being saved:" + %ul + - @admin_project.errors.full_messages.each do |msg| + %li= msg + + .span-24 + .span-12 + .field + = f.label :name + %br + = f.text_field :name + .field + = f.label :code + %br + = f.text_field :code + .field + = f.label :path + %br + = f.text_field :path + .span-10 + .field + = f.label :description + %br + = f.text_area :description + .clear + .actions + = f.submit 'Save', :class => "lbutton" diff --git a/app/views/admin/projects/edit.html.haml b/app/views/admin/projects/edit.html.haml new file mode 100644 index 00000000000..9d9a1ee0a3f --- /dev/null +++ b/app/views/admin/projects/edit.html.haml @@ -0,0 +1,5 @@ += render 'form' + += link_to 'Show', [:admin, @admin_project] +\| += link_to 'Back', admin_projects_path diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml new file mode 100644 index 00000000000..55c41ec31bb --- /dev/null +++ b/app/views/admin/projects/index.html.haml @@ -0,0 +1,26 @@ +%table + %tr + %th Name + %th Code + %th Path + %th Team Members + %th Last Commit + %th + %th + %th + + - @admin_projects.each do |project| + %tr + %td= project.name + %td= project.code + %td= project.path + %td= project.users_projects.count + %td= last_commit(project) + %td= link_to 'Show', [:admin, project] + %td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}" + %td= link_to 'Destroy', [:admin, project], :confirm => 'Are you sure?', :method => :delete + +%br + += paginate @admin_projects += link_to 'New Project', new_admin_project_path diff --git a/app/views/admin/projects/new.html.haml b/app/views/admin/projects/new.html.haml new file mode 100644 index 00000000000..3a4d50e19f9 --- /dev/null +++ b/app/views/admin/projects/new.html.haml @@ -0,0 +1,5 @@ +%h1 New project + += render 'form' + += link_to 'Back', admin_projects_path diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml new file mode 100644 index 00000000000..6891894122c --- /dev/null +++ b/app/views/admin/projects/show.html.haml @@ -0,0 +1,45 @@ +%p#notice= notice + +.span-8.colborder + %h2= @admin_project.name + + %p + %b Name: + = @admin_project.name + %p + %b Code: + = @admin_project.code + %p + %b Path: + = @admin_project.path + %p + %b Description: + = @admin_project.description + + = link_to 'Edit', edit_admin_project_path(@admin_project) + \| + = link_to 'Back', admin_projects_path + +.span-14 + + %h2 Team + + %table.round-borders + %tr + %th Name + %th Added + %th Web + %th Git + %th Admin + %th + + - @admin_project.users_projects.each do |tm| + %tr + %td= link_to tm.user_name, admin_team_member_path(tm) + %td= time_ago_in_words(tm.updated_at) + " ago" + %td= check_box_tag "read", 1, @admin_project.readers.include?(tm.user), :disabled => :disabled + %td= check_box_tag "commit", 1, @admin_project.writers.include?(tm.user), :disabled => :disabled + %td.span-2= check_box_tag "admin", 1, @admin_project.admins.include?(tm.user), :disabled => :disabled + %td= link_to 'Destroy', admin_team_member_path(tm), :confirm => 'Are you sure?', :method => :delete + + = link_to 'New Team Member', new_admin_team_member_path(:team_member => {:project_id => @admin_project.id}) diff --git a/app/views/admin/team_members/_form.html.haml b/app/views/admin/team_members/_form.html.haml new file mode 100644 index 00000000000..ab48c513a86 --- /dev/null +++ b/app/views/admin/team_members/_form.html.haml @@ -0,0 +1,34 @@ += form_for @admin_team_member, :as => :team_member, :url => @admin_team_member.new_record? ? admin_team_members_path(@admin_team_member) : admin_team_member_path(@admin_team_member) do |f| + -if @admin_team_member.errors.any? + #error_explanation + %h2= "#{pluralize(@admin_team_member.errors.count, "error")} prohibited this admin_project from being saved:" + %ul + - @admin_team_member.errors.full_messages.each do |msg| + %li= msg + + .span-10 + - if @admin_team_member.new_record? + .field + = f.label :user_id + %br + = f.select :user_id, User.all.map { |user| [user.name, user.id] } + .field + = f.label :project_id + %br + = f.select :project_id, Project.all.map { |user| [user.name, user.id] } + + .span-10 + .span-6 + %b Access: + .span-8 + = f.check_box :read + Web Access (Browse Repo) + .span-8 + = f.check_box :write + Git Access (User will be added to commiters list) + .span-6.append-bottom + = f.check_box :admin + Admin (Can manage project) + %hr + .actions + = f.submit 'Save' diff --git a/app/views/admin/team_members/edit.html.haml b/app/views/admin/team_members/edit.html.haml new file mode 100644 index 00000000000..d0362812834 --- /dev/null +++ b/app/views/admin/team_members/edit.html.haml @@ -0,0 +1,5 @@ += render 'form' + += link_to 'Show', admin_team_member_path(@admin_team_member) +\| += link_to 'Back', admin_team_members_path diff --git a/app/views/admin/team_members/index.html.haml b/app/views/admin/team_members/index.html.haml new file mode 100644 index 00000000000..4076917da64 --- /dev/null +++ b/app/views/admin/team_members/index.html.haml @@ -0,0 +1,30 @@ +- @admin_team_members.group_by(&:project).sort.each do |project, members| + %h3= link_to project.name, [:admin, project] + %table + %tr + %th Name + %th Email + %th Read + %th Git + %th Manage + %th Added + %th + %th + %th + - members.each do |tm| + - user = tm.user + %tr + %td.span-6= tm.user_name + %td.span-6= tm.user_email + %td.span-1= check_box_tag "read", 1, project.readers.include?(user), :disabled => :disabled + %td.span-1= check_box_tag "commit", 1, project.writers.include?(user), :disabled => :disabled + %td.span-2= check_box_tag "admin", 1, project.admins.include?(user), :disabled => :disabled + %td.span-3= time_ago_in_words(tm.updated_at) + " ago" + %td= link_to 'Show', admin_team_member_path(tm) + %td= link_to 'Edit', edit_admin_team_member_path(tm), :id => "edit_#{dom_id(tm)}" + %td= link_to 'Destroy', admin_team_member_path(tm), :confirm => 'Are you sure?', :method => :delete + +%br + += paginate @admin_team_members += link_to 'New Team Member', new_admin_team_member_path diff --git a/app/views/admin/team_members/new.html.haml b/app/views/admin/team_members/new.html.haml new file mode 100644 index 00000000000..1321f42e7db --- /dev/null +++ b/app/views/admin/team_members/new.html.haml @@ -0,0 +1,5 @@ +%h1 New team member + += render 'form' + += link_to 'Back', admin_team_members_path diff --git a/app/views/admin/team_members/show.html.haml b/app/views/admin/team_members/show.html.haml new file mode 100644 index 00000000000..bd30c7b0e6c --- /dev/null +++ b/app/views/admin/team_members/show.html.haml @@ -0,0 +1,32 @@ +%p#notice= notice + +.span-10 + %p + %b Name: + = @admin_team_member.user_name + %p + %b Project: + = @admin_team_member.project.name + %p + %b Since: + = @admin_team_member.updated_at + + +.span-10 + .span-6 + %b Access: + .span-8 + = check_box_tag "read", 1, @admin_team_member.read, :disabled => :disabled + Web Access (Browse Repo) + .span-8 + = check_box_tag "commit", 1, @admin_team_member.write, :disabled => :disabled + Git Access (User will be added to commiters list) + .span-6.append-bottom + = check_box_tag "admin", 1, @admin_team_member.admin, :disabled => :disabled + Admin (Can manage project) + +%hr + += link_to 'Edit', edit_admin_team_member_path(@admin_project) +\| += link_to 'Back', admin_team_members_path diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml new file mode 100644 index 00000000000..17be416fa1d --- /dev/null +++ b/app/views/admin/users/_form.html.haml @@ -0,0 +1,38 @@ +.user_new + = form_for [:admin, @admin_user] do |f| + -if @admin_user.errors.any? + #error_explanation + %h2= "#{pluralize(@admin_user.errors.count, "error")} prohibited this admin_user from being saved:" + %ul + - @admin_user.errors.full_messages.each do |msg| + %li= msg + + .span-24 + .span-11.colborder + .field + = f.label :name + %br + = f.text_field :name + .field + = f.label :email + %br + = f.text_field :email + .field + = f.label :password + %br + = f.password_field :password + .field + = f.label :password_confirmation + %br + = f.password_field :password_confirmation + .span-11 + .field.prepend-top.append-bottom + = f.check_box :admin + = f.label :admin + .field.prepend-top + = f.text_field :projects_limit, :class => "small_input" + = f.label :projects_limit + .clear + %br + .actions + = f.submit 'Save', :class => "lbutton" diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml new file mode 100644 index 00000000000..fac995175fe --- /dev/null +++ b/app/views/admin/users/edit.html.haml @@ -0,0 +1,4 @@ += render 'form' + += link_to 'Show', [:admin, @admin_user], :class => "right lbutton" += link_to 'Back', admin_users_path, :class => "right lbutton" diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml new file mode 100644 index 00000000000..db60a613bce --- /dev/null +++ b/app/views/admin/users/index.html.haml @@ -0,0 +1,24 @@ +%table + %tr + %th Admin + %th Name + %th Email + %th Projects + %th + %th + %th + + - @admin_users.each do |user| + %tr + %td= check_box_tag "admin", 1, user.admin, :disabled => :disabled + %td= user.name + %td= user.email + %td= user.users_projects.count + %td= link_to 'Show', [:admin, user] + %td= link_to 'Edit', edit_admin_user_path(user), :id => "edit_#{dom_id(user)}" + %td= link_to 'Destroy', [:admin, user], :confirm => 'Are you sure?', :method => :delete + +%br + += paginate @admin_users += link_to 'New User', new_admin_user_path diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml new file mode 100644 index 00000000000..cec6e3e188c --- /dev/null +++ b/app/views/admin/users/new.html.haml @@ -0,0 +1,6 @@ +%h1 New user + += render 'form' + + += link_to 'Back', admin_users_path, :class => "right lbutton" diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml new file mode 100644 index 00000000000..b1d110bec27 --- /dev/null +++ b/app/views/admin/users/show.html.haml @@ -0,0 +1,45 @@ +%p#notice= notice + +.span-8.colborder + %p + %b Name: + = @admin_user.name + %p + %b Email: + = @admin_user.email + %p + %b Admin: + = @admin_user.admin + %p + %b Projects limit: + = @admin_user.projects_limit + + .clear + = link_to 'Edit', edit_admin_user_path(@admin_user) + \| + = link_to 'Back', admin_users_path + +.span-14 + %h2 Projects + + %table.round-borders + %tr + %th Name + %th Added + %th Web + %th Git + %th Admin + %th + + - @admin_user.users_projects.each do |tm| + - project = tm.project + %tr + %td= link_to project.name, admin_project_path(project) + %td= time_ago_in_words(tm.updated_at) + " ago" + %td= check_box_tag "read", 1, project.readers.include?(@admin_user), :disabled => :disabled + %td= check_box_tag "commit", 1, project.writers.include?(@admin_usertm), :disabled => :disabled + %td.span-2= check_box_tag "admin", 1, project.admins.include?(@admin_user), :disabled => :disabled + %td= link_to 'Edit', edit_admin_team_member_path(tm) + %td= link_to 'Cancel', admin_team_member_path(tm), :confirm => 'Are you sure?', :method => :delete + + = link_to 'Add To Another Project', new_admin_team_member_path(:team_member => {:user_id => @admin_user.id}) diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml new file mode 100644 index 00000000000..4eebb83aa15 --- /dev/null +++ b/app/views/commits/_commits.html.haml @@ -0,0 +1,22 @@ +- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| + .day-commits-table + .day-header + %h3= day.stamp("28 Aug, 2010") + %ul + - commits.each do |commit| + %li{ :class => "commit", :url => project_commit_path(@project, :id => commit.id) } + - if commit.author.email + = image_tag gravatar_icon(commit.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" + - else + = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" + %p + %strong + = commit.message.length > 60 ? (commit.message[0..59] + "...") : commit.message + = link_to "Browse Code", tree_project_path(@project, :commit_id => commit.id), :class => "lite_button", :style => "float:right" + = link_to truncate(commit.id.to_s, :length => 16), project_commit_path(@project, :id => commit.id), :class => "lite_button", :style => "width:120px;float:right" + %span + %span + [ #{commit.author} ] + = time_ago_in_words(commit.committed_date) + ago += more_commits_link if @commits.size > 99 diff --git a/app/views/commits/_diff.html.haml b/app/views/commits/_diff.html.haml new file mode 100644 index 00000000000..dff99bf12f0 --- /dev/null +++ b/app/views/commits/_diff.html.haml @@ -0,0 +1,58 @@ +- require "utils" +.file_stats + - @commit.diffs.each do |diff| + - if diff.deleted_file + %span.removed_file + %a{:href => "##{diff.a_path}"} + = diff.a_path + = image_tag "blueprint_delete.png" + - elsif diff.renamed_file + %span.moved_file + %a{:href => "##{diff.b_path}"} + = diff.a_path + = "->" + = diff.b_path + = image_tag "blueprint_notice.png" + - elsif diff.new_file + %span.new_file + %a{:href => "##{diff.b_path}"} + = diff.b_path + = image_tag "blueprint_add.png" + - else + %span.edit_file + %a{:href => "##{diff.b_path}"} + = diff.b_path + = image_tag "blueprint_info.png" +- @commit.diffs.each do |diff| + - next if diff.diff.empty? + - file = (@commit.tree / diff.b_path) + - next unless file + .diff_file + .diff_file_header + - if diff.deleted_file + %strong{:id => "#{diff.b_path}"}= diff.a_path + - else + %strong{:id => "#{diff.b_path}"}= diff.b_path + %br/ + .diff_file_content + - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + - lines_arr = diff.diff.lines.to_a + - line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 + - line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 + - lines = lines_arr[3..-1].join + - lines.each_line do |line| + = diff_line(line, line_new, line_old) + - if line[0] == "+" + - line_new += 1 + - elsif + - line[0] == "-" + - line_old += 1 + - else + - line_new += 1 + - line_old += 1 + - elsif file.mime_type =~ /image/ + .diff_file_content_image + %img{:src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + - else + %p + %center No preview for this file type diff --git a/app/views/commits/_index.html.haml b/app/views/commits/_index.html.haml new file mode 100644 index 00000000000..e4c9cc6e83c --- /dev/null +++ b/app/views/commits/_index.html.haml @@ -0,0 +1,9 @@ += form_tag project_commits_path(@project), :method => :get do + %h3 + = @project.name + [ #{select_tag "branch", options_for_select(@repo.heads.map(&:name), @branch), :onchange => "this.form.submit();", :class => "small"} ] += link_to 'Back', project_path(@project), :class => "button" +%h1 Listing commits +%div{:id => dom_id(@project)} + = render "commits" +%br/ \ No newline at end of file diff --git a/app/views/commits/index.html.haml b/app/views/commits/index.html.haml new file mode 100644 index 00000000000..95579119aee --- /dev/null +++ b/app/views/commits/index.html.haml @@ -0,0 +1,13 @@ +%div + %h3 + .left + = form_tag project_commits_path(@project), :method => :get do + = select_tag "branch", options_for_select(@repo.heads.map(&:name), @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Branches" +   + .left.prepend-1 + = form_tag project_commits_path(@project), :method => :get do + = select_tag "tag", options_for_select(@project.tags, @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" + = text_field_tag "ssh", @project.url_to_repo, :class => ["ssh_project_url", "one_click_select"] + .clear + %div{:id => dom_id(@project)} + = render "commits" diff --git a/app/views/commits/index.js.erb b/app/views/commits/index.js.erb new file mode 100644 index 00000000000..94daa396c5c --- /dev/null +++ b/app/views/commits/index.js.erb @@ -0,0 +1,2 @@ +$("#more-commits-link").remove(); +$('#<%= dom_id(@project)%>').append('<%= escape_javascript(render("commits")) %>'); diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml new file mode 100644 index 00000000000..147aaafb855 --- /dev/null +++ b/app/views/commits/show.html.haml @@ -0,0 +1,39 @@ +%h3 + = "[ #{@commit.committer} ] #{truncate @commit.message, :length => 80}" +-#= link_to 'Back', project_commits_path(@project), :class => "button" +%table.round-borders + %tr + %td ID + %td= @commit.id + %tr + %td Author + %td= @commit.author + %tr + %td Commiter + %td= @commit.committer + %tr + %td Commited Date + %td= @commit.committed_date + %tr + %td Message + %td= @commit.message + %tr + %td Tree + %td= link_to 'Browse Code', tree_project_path(@project, :commit_id => @commit.id) +.clear + +#tabs + %ul + %li + %a{ :href => "#tabs-1" } Diff + %li + %a{ :href => "#tabs-2" } Comments + %span{ :class => "notes_count" }= @notes.count + %hr + #tabs-1 + = render "commits/diff" + #tabs-2 + = render "notes/notes" + +:javascript + $(function() { $( "#tabs" ).tabs(); }); diff --git a/app/views/commits/show.js.haml b/app/views/commits/show.js.haml new file mode 100644 index 00000000000..2c46689b1ed --- /dev/null +++ b/app/views/commits/show.js.haml @@ -0,0 +1,6 @@ +:plain + $("#side-commit-preview").remove(); + var side = $("
      "); + side.html("#{escape_javascript(render "commits/show")}"); + $("##{dom_id(@project)}").parent().append(side); + $("##{dom_id(@project)}").addClass("span-14"); diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml new file mode 100644 index 00000000000..f4ce9a17d82 --- /dev/null +++ b/app/views/dashboard/index.html.haml @@ -0,0 +1 @@ +timeline diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 00000000000..b7ae403caa5 --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,12 @@ +

      Resend confirmation instructions

      + +<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
      <%= f.label :email %>
      + <%= f.email_field :email %>
      + +
      <%= f.submit "Resend confirmation instructions" %>
      +<% end %> + +<%= render :partial => "devise/shared/links" %> \ No newline at end of file diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 00000000000..a6ea8ca17e8 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

      Welcome <%= @resource.email %>!

      + +

      You can confirm your account through the link below:

      + +

      <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

      diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 00000000000..ae9e888abb9 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

      Hello <%= @resource.email %>!

      + +

      Someone has requested a link to change your password, and you can do this through the link below.

      + +

      <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

      + +

      If you didn't request this, please ignore this email.

      +

      Your password won't change until you access the link above and create a new one.

      diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 00000000000..2263c219522 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

      Hello <%= @resource.email %>!

      + +

      Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

      + +

      Click the link below to unlock your account:

      + +

      <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

      diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 00000000000..e75c937101a --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,16 @@ +

      Change your password

      + +<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> + <%= devise_error_messages! %> + <%= f.hidden_field :reset_password_token %> + +
      <%= f.label :password, "New password" %>
      + <%= f.password_field :password %>
      + +
      <%= f.label :password_confirmation, "Confirm new password" %>
      + <%= f.password_field :password_confirmation %>
      + +
      <%= f.submit "Change my password" %>
      +<% end %> + +<%= render :partial => "devise/shared/links" %> \ No newline at end of file diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 00000000000..bccf0181769 --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,15 @@ +

      Forgot your password?

      + +
      + <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
      <%= f.label :email %>
      + <%= f.email_field :email %>
      + +
      <%= f.submit "Send me reset password instructions", :class => "lbutton vm" %>
      + <% end %> +
      +
      + <%= render :partial => "devise/shared/links" %> +
      diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 00000000000..dd26e8a47b8 --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb @@ -0,0 +1,28 @@ +

      Edit <%= resource_name.to_s.humanize %>

      + +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> + <%= devise_error_messages! %> + +
      <%= f.label :email %>
      + <%= f.email_field :email %>
      + +
      <%= f.label :name %>
      + <%= f.text_field :name %>
      + +
      <%= f.label :password %> (leave blank if you don't want to change it)
      + <%= f.password_field :password %>
      + +
      <%= f.label :password_confirmation %>
      + <%= f.password_field :password_confirmation %>
      + +
      <%= f.label :current_password %> (we need your current password to confirm your changes)
      + <%= f.password_field :current_password %>
      + +
      <%= f.submit "Update", :class => "input_button" %>
      +<% end %> + +

      Cancel my account

      + +

      Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.

      + +<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 00000000000..4ac617c5912 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,18 @@ +

      Sign up

      + +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> + <%= devise_error_messages! %> + +
      <%= f.label :email %>
      + <%= f.email_field :email %>
      + +
      <%= f.label :password %>
      + <%= f.password_field :password %>
      + +
      <%= f.label :password_confirmation %>
      + <%= f.password_field :password_confirmation %>
      + +
      <%= f.submit "Sign up", :class => "input_button" %>
      +<% end %> + +<%= render :partial => "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 00000000000..21de54f0c4c --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,20 @@ +

      Sign in

      + +
      + <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> +
      <%= f.label :email %>
      + <%= f.text_field :email %>
      + +
      <%= f.label :password %>
      + <%= f.password_field :password %>
      + + <% if devise_mapping.rememberable? -%> +
      <%= f.check_box :remember_me %> <%= f.label :remember_me %>
      + <% end -%> +
      +
      <%= f.submit "Sign in", :class => "lbutton vm" %>
      + <% end %> +
      +
      + <%= render :partial => "devise/shared/links" %> +
      diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb new file mode 100644 index 00000000000..eab783a4c92 --- /dev/null +++ b/app/views/devise/shared/_links.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to "Sign in", new_session_path(resource_name) %>
      +<% end -%> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to "Sign up", new_registration_path(resource_name) %>
      +<% end -%> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' %> + <%= link_to "Forgot your password?", new_password_path(resource_name) %>
      +<% end -%> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
      +<% end -%> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> + <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
      +<% end -%> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> + <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
      + <% end -%> +<% end -%> \ No newline at end of file diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 00000000000..c6cdcfe51ce --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,12 @@ +

      Resend unlock instructions

      + +<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
      <%= f.label :email %>
      + <%= f.email_field :email %>
      + +
      <%= f.submit "Resend unlock instructions" %>
      +<% end %> + +<%= render :partial => "devise/shared/links" %> \ No newline at end of file diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml new file mode 100644 index 00000000000..71acdba1895 --- /dev/null +++ b/app/views/issues/_form.html.haml @@ -0,0 +1,24 @@ +%div + = form_for [@project, @issue], :remote => "true" do |f| + -if @issue.errors.any? + %ul + - @issue.errors.full_messages.each do |msg| + %li= msg + + .span-6 + = f.label :title + = f.text_field :title, :style => "width:450px" + .span-6 + = f.label :content + = f.text_area :content, :style => "width:450px; height:130px" + .span-6.append-bottom + = f.label :assignee_id + = f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }) + - unless @issue.new_record? + .span-3.right + = f.label :closed + %br + = f.check_box :closed + %hr + .span-6 + = f.submit 'Save', :class => "lbutton vm" diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml new file mode 100644 index 00000000000..d3e21940346 --- /dev/null +++ b/app/views/issues/_issues.html.haml @@ -0,0 +1,10 @@ +%table.round-borders#issues-table + %tr + %th Assignee + %th ID + %th Title + %th Closed? + %th + + - @issues.each do |issue| + = render(:partial => 'show', :locals => {:issue => issue}) diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml new file mode 100644 index 00000000000..131d0d4c8a2 --- /dev/null +++ b/app/views/issues/_show.html.haml @@ -0,0 +1,18 @@ +%tr{ :id => dom_id(issue), :class => "issue", :url => project_issue_path(@project, issue) } + %td + = image_tag gravatar_icon(issue.assignee.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = truncate issue.assignee.name, :lenght => 20 + %td ##{issue.id} + %td= html_escape issue.title + %td + - if can? current_user, :write_issue, @project + = form_for([@project, issue], :remote => true) do |f| + = f.check_box :closed, :onclick => "$(this).parent().submit();" + = hidden_field_tag :status_only, true + - else + = check_box_tag "closed", 1, issue.closed, :disabled => true + %td + - if can?(current_user, :admin_issue, @project) || issue.author == current_user + = link_to 'Edit', edit_project_issue_path(@project, issue), :class => "lbutton positive", :remote => true + - if can?(current_user, :admin_issue, @project) || issue.author == current_user + = link_to 'Destroy', [@project, issue], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-issue negative", :id => "destroy_issue_#{issue.id}" diff --git a/app/views/issues/create.js.haml b/app/views/issues/create.js.haml new file mode 100644 index 00000000000..3713a8aff41 --- /dev/null +++ b/app/views/issues/create.js.haml @@ -0,0 +1,9 @@ +- if @issue.valid? + :plain + $("#new_issue_dialog").dialog("close"); + $.ajax({type: "GET", url: location.href, dataType: "script"}); +- else + :plain + $("#new_issue_dialog").empty(); + $("#new_issue_dialog").append("#{escape_javascript(render('form'))}"); + $('select#issue_assignee_id').selectmenu({width:300}); diff --git a/app/views/issues/edit.js.haml b/app/views/issues/edit.js.haml new file mode 100644 index 00000000000..f08e3217a89 --- /dev/null +++ b/app/views/issues/edit.js.haml @@ -0,0 +1,12 @@ +:plain + var edit_issue_dialog = $("
      "); + edit_issue_dialog.html("#{escape_javascript(render('form'))}"); + $(edit_issue_dialog).dialog({ + width: 500, + resizable: false, + draggable: false, + title: "Issue ##{@issue.id} #{"[CLOSED]" if @issue.closed}", + close: function(event, ui) { $("#edit_issue_dialog").remove();}, + modal: true + }); + $('select#issue_assignee_id').selectmenu({width:300}); diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml new file mode 100644 index 00000000000..7157d2c385d --- /dev/null +++ b/app/views/issues/index.html.haml @@ -0,0 +1,24 @@ +%div + - if can? current_user, :write_issue, @project + .left= link_to 'New Issue', new_project_issue_path(@project), :remote => true, :class => "lbutton vm" + .right + = form_tag project_issues_path(@project), :method => :get do + .span-2 + = radio_button_tag :f, 0, (params[:f] || "0") == "0", :onclick => "this.form.submit()", :id => "open_issues" + = label_tag "open_issues","Open" + .span-2 + = radio_button_tag :f, 2, params[:f] == "2", :onclick => "this.form.submit()", :id => "closed_issues" + = label_tag "closed_issues","Closed" + .span-2 + = radio_button_tag :f, 3, params[:f] == "3", :onclick => "this.form.submit()", :id => "my_issues" + = label_tag "my_issues","To Me" + + .span-2 + = radio_button_tag :f, 1, params[:f] == "1", :onclick => "this.form.submit()", :id => "all_issues" + = label_tag "all_issues","All" + + #issues-table-holder= render "issues" + %br +:javascript + $('.delete-issue').live('ajax:success', function() { + $(this).closest('tr').fadeOut(); }); diff --git a/app/views/issues/index.js.haml b/app/views/issues/index.js.haml new file mode 100644 index 00000000000..1f051309233 --- /dev/null +++ b/app/views/issues/index.js.haml @@ -0,0 +1,2 @@ +:plain + $('#issues-table-holder').html("#{escape_javascript(render('issues'))}"); diff --git a/app/views/issues/new.js.haml b/app/views/issues/new.js.haml new file mode 100644 index 00000000000..530641440a7 --- /dev/null +++ b/app/views/issues/new.js.haml @@ -0,0 +1,12 @@ +:plain + var new_issue_dialog = $("
      "); + new_issue_dialog.html("#{escape_javascript(render('form'))}"); + $(new_issue_dialog).dialog({ + width: 500, + resizable: false, + draggable: false, + title: "Add new issue", + modala: true, + close: function(event, ui) { $("#new_issue_dialog").remove();} + }); + $('select#issue_assignee_id').selectmenu({width:300}); diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml new file mode 100644 index 00000000000..199c80c8617 --- /dev/null +++ b/app/views/issues/show.html.haml @@ -0,0 +1,44 @@ +%h2 + = "Issue ##{@issue.id} - #{@issue.title}" + +.span-15 + = simple_format html_escape(@issue.content) + .issue_notes= render "notes/notes" +.span-8.right + .span-8 + - if @issue.closed + %center.success Closed + - else + %center.error Open + %table.round-borders + %tr + %td Title: + %td + = truncate html_escape(@issue.title) + %tr + %td Project + %td + %strong= @issue.project.name + %tr + %td Author: + %td + = image_tag gravatar_icon(@issue.author.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = @issue.author.name + %tr + %td Assignee: + %td + = image_tag gravatar_icon(@issue.assignee.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = @issue.assignee.name + %tr + %td Closed? + %td + - if can? current_user, :write_issue, @project + = form_for([@project, @issue]) do |f| + = f.check_box :closed, :onclick => "$(this).parent().submit();" + = hidden_field_tag :status_only, true + - else + = check_box_tag "closed", 1, @issue.closed, :disabled => true + + +.clear + diff --git a/app/views/issues/update.js.haml b/app/views/issues/update.js.haml new file mode 100644 index 00000000000..c365c3f5d55 --- /dev/null +++ b/app/views/issues/update.js.haml @@ -0,0 +1,14 @@ +- if params[:status_only] + - if @issue.valid? + :plain + $("##{dom_id(@issue)}").fadeOut(); +- else + - if @issue.valid? + :plain + $("#edit_issue_dialog").dialog("close"); + $.ajax({type: "GET", url: location.href, dataType: "script"}); + - else + :plain + $("#edit_issue_dialog").empty(); + $("#edit_issue_dialog").append("#{escape_javascript(render('form'))}"); + $('select#issue_assignee_id').selectmenu({width:300}); diff --git a/app/views/keys/_form.html.haml b/app/views/keys/_form.html.haml new file mode 100644 index 00000000000..7d3e14efc1e --- /dev/null +++ b/app/views/keys/_form.html.haml @@ -0,0 +1,16 @@ +%div + = form_for @key, :remote => true do |f| + -if @key.errors.any? + %ul + - @key.errors.full_messages.each do |msg| + %li= msg + + .span-6 + = f.label :title + = f.text_field :title, :style => "width:300px" + .span-6 + = f.label :key + = f.text_area :key, :style => "width:300px; height:130px" + .span-6 + = f.submit 'Save', :class => "lbutton vm" + diff --git a/app/views/keys/_show.html.haml b/app/views/keys/_show.html.haml new file mode 100644 index 00000000000..a4e236256d3 --- /dev/null +++ b/app/views/keys/_show.html.haml @@ -0,0 +1,4 @@ +%tr + %td= truncate key.title, :lenght => 12 + %td= truncate key.key, :lenght => 1114 + %td= link_to 'Cancel', key, :confirm => 'Are you sure?', :method => :delete, :class => "lbutton negative delete-key", :id => "destroy_key_#{key.id}", :remote => true diff --git a/app/views/keys/create.js.haml b/app/views/keys/create.js.haml new file mode 100644 index 00000000000..7b92be77180 --- /dev/null +++ b/app/views/keys/create.js.haml @@ -0,0 +1,8 @@ +- if @key.valid? + :plain + $("#new_key_dialog").dialog("close"); + $("#keys-table").append("#{escape_javascript(render(:partial => 'show', :locals => {:key => @key} ))}"); +- else + :plain + $("#new_key_dialog").empty(); + $("#new_key_dialog").append("#{escape_javascript(render('form'))}"); diff --git a/app/views/keys/edit.html.haml b/app/views/keys/edit.html.haml new file mode 100644 index 00000000000..60a3afedddc --- /dev/null +++ b/app/views/keys/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing key + += render 'form' + += link_to 'Show', @key +\| += link_to 'Back', keys_path diff --git a/app/views/keys/index.html.haml b/app/views/keys/index.html.haml new file mode 100644 index 00000000000..416ae8f8f95 --- /dev/null +++ b/app/views/keys/index.html.haml @@ -0,0 +1,15 @@ +%div#new-key-holder + = link_to "Add new", new_key_path, :remote => true, :class => "lbutton vm" + +%table.round-borders#keys-table + %tr + %th title + %th key + %th Actions + - @keys.each do |key| + = render(:partial => 'show', :locals => {:key => key}) + +:javascript + $('.delete-key').live('ajax:success', function() { + $(this).closest('tr').fadeOut(); }); + diff --git a/app/views/keys/new.html.haml b/app/views/keys/new.html.haml new file mode 100644 index 00000000000..fed448b4e09 --- /dev/null +++ b/app/views/keys/new.html.haml @@ -0,0 +1,5 @@ +%h1 New key + += render 'form' + += link_to 'Back', keys_path diff --git a/app/views/keys/new.js.haml b/app/views/keys/new.js.haml new file mode 100644 index 00000000000..86e9db030c5 --- /dev/null +++ b/app/views/keys/new.js.haml @@ -0,0 +1,11 @@ +:plain + var new_key_dialog = $("
      "); + new_key_dialog.html("#{escape_javascript(render('form'))}"); + $(new_key_dialog).dialog({ + width: 350, + resizable: false, + draggable: false, + title: "Add new public key", + close: function(event, ui) { $("#new_key_dialog").remove();}, + modal: true + }); diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml new file mode 100644 index 00000000000..4b6eb2b500e --- /dev/null +++ b/app/views/layouts/_flash.html.haml @@ -0,0 +1,18 @@ +- if alert || notice + - text = alert || notice + %div{:style => "display:none", :id => "flash_container"} + .container + %center + %h4= text + :javascript + $(function(){ + $("#flash_container").slideDown("slow"); + $("#flash_container").click(function(){ + $(this).slideUp("slow"); + }); + setTimeout("hideFlash()",2000); + }); + + function hideFlash(){ + $("#flash_container").slideUp("slow"); + } diff --git a/app/views/layouts/_head_panel.html.erb b/app/views/layouts/_head_panel.html.erb new file mode 100644 index 00000000000..f32e4bc2b71 --- /dev/null +++ b/app/views/layouts/_head_panel.html.erb @@ -0,0 +1,34 @@ +
      +
      +
      +
      + <%#= image_tag "git.png", :height => 40, :class => "left" %> + <%#= link_to "gitlab", root_path, :id => "logo" %> + + <%= text_field_tag "search", nil, :placeholder => "Search" %> + +
      +
      + <%= link_to truncate(@project.name, :length => 20), project_path(@project), :class => "current button" if @project && !@project.new_record? %> + <%= link_to 'Home', root_path, :class => current_page?(root_url) ? "current button" : "button" %> + <%= link_to 'Projects', projects_path, :class => current_page?(projects_path) ? "current button" : "button" %> + <%= link_to 'Profile', profile_path, :class => (controller.controller_name == "keys") ? "current button" : "button" %> + <%= link_to('Admin', admin_root_path, :class => admin_namespace? ? "current button" : "button" ) if current_user.is_admin? %> + <%#= link_to 'Profile', edit_user_registration_path, :class => "button" %> + <%= link_to 'Logout', destroy_user_session_path, :class => "button", :method => :delete %> +
      +
      +
      +
      + + +<% if current_user %> + <%= javascript_tag do %> + $(function() { + $("#search" ).autocomplete({ + source: <%= raw search_autocomplete_source %>, + select: function(event, ui) { location.href = ui.item.url } + }); + }); + <% end %> +<% end %> diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 00000000000..3eb99da61e5 --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,29 @@ +!!! +%html + %head + %title + GitLab #{" - #{@project.name}" if @project && !@project.new_record?} + = stylesheet_link_tag 'blueprint/screen', :media => "screen, projection" + = stylesheet_link_tag 'blueprint/print', :media => "print" + = stylesheet_link_tag 'blueprint/plugins/buttons/screen', :media => "screen, projection" + = stylesheet_link_tag 'blueprint/plugins/link-icons/screen', :media => "screen, projection" + = stylesheet_link_tag 'jquery_ui/jquery-ui-1.8.16.custom', :media => "screen, projection" + = stylesheet_link_tag "application" + = javascript_include_tag "application" + = csrf_meta_tags + %link{:href => "/assets/favicon.png", :rel => "icon", :type => "image/png"}/ + = javascript_tag do + REQ_URI = "#{request.env["REQUEST_URI"]}"; + REQ_REFFER = "#{request.env["HTTP_REFERER"]}"; + %body#thebody + = render :partial => "layouts/flash" + - if user_signed_in? + = render :partial => "layouts/head_panel" + .top_bar.container + = render :partial => "projects/top_menu" if @project && !@project.new_record? + = render :partial => "projects/projects_top_menu" if (controller.controller_name == "projects" && ["index", "new", "create"].include?(controller.action_name)) && !admin_namespace? + = render :partial => "profile/top_menu" if ["keys", "profile"].include?(controller.controller_name) + = render :partial => "admin/top_menu" if admin_namespace? + #content-container.container + .span-24 + = yield diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml new file mode 100644 index 00000000000..0cef736c80a --- /dev/null +++ b/app/views/layouts/notify.html.haml @@ -0,0 +1,36 @@ +%html{:lang => "en"} + %head + %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"} + %title + gitlabhq + :css + .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;} + .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;} + .content h2 {color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; } + .content p {color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif;} + .content a {color: #0eb6ce; text-decoration: none;} + .footer p {font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;} + .footer a {color: #0eb6ce; text-decoration: none;} + %body{:bgcolor => "#EAEAEA", :style => "margin: 0; padding: 0; background: #EAEAEA"} + %table{:align => "center", :border => "0", :cellpadding => "0", :cellspacing => "0", :style => "padding: 35px 0; background: #EAEAEA;", :width => "100%"} + %tr + %td{:align => "center", :style => "margin: 0; padding: 0; background: #EAEAEA;"} + %table.header{:align => "center", :border => "0", :cellpadding => "0", :cellspacing => "0", :style => "font-family: Helvetica, Arial, sans-serif; background:#333", :width => "600"} + %tr + %td{:style => "font-size: 0px;", :width => "20"} + \  + %td{:align => "left", :style => "padding: 18px 0 10px;", :width => "580"} + %h1{:style => "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"} + gitlab + - if @project + | #{@project.name} + %table{:align => "center", :bgcolor => "#fff", :border => "0", :cellpadding => "0", :cellspacing => "0", :style => "font-family: Helvetica, Arial, sans-serif; background: #fff;", :width => "600"} + %tr= yield + %tr + %td{:align => "left", :colspan => "2", :height => "3", :style => "padding: font-size: 0; line-height: 0; height: 3px;", :width => "600"} + %table.footer{:align => "center", :border => "0", :cellpadding => "0", :cellspacing => "0", :style => "font-family: Helvetica, Arial, sans-serif; line-height: 10px;", :width => "600"} + %tr + %td{:align => "center", :style => "padding: 5px 0 10px; font-size: 11px; color:#7d7a7a; margin: 0; line-height: 1.2;font-family: Helvetica, Arial, sans-serif;", :valign => "top"} + %br + %p{:style => "font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;"} + You're receiving this newsletter because you are in project team. diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml new file mode 100644 index 00000000000..ccb159f128c --- /dev/null +++ b/app/views/notes/_form.html.haml @@ -0,0 +1,28 @@ +%div + = form_for [@project, @note], :remote => "true", :multipart => true do |f| + -if @note.errors.any? + .errors.error + - @note.errors.full_messages.each do |msg| + %div= msg + + = f.hidden_field :noteable_id + = f.hidden_field :noteable_type + + %div + = f.label :note + %cite (255 symbols only) + %br + = f.text_area :note, :style => "width:97%;height:100px", :size => 255 + + %div + = f.label :attachment + %cite (less than 10 MB) + %br + = f.file_field :attachment + + = check_box_tag :notify, 1, true + = label_tag :notify, "Notify project team about your note" + + .clear + %br + = f.submit 'Add note', :class => "lbutton vm" diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml new file mode 100644 index 00000000000..457bb8db763 --- /dev/null +++ b/app/views/notes/_notes.html.haml @@ -0,0 +1,14 @@ +%ul#notes-list + - @notes.each do |note| + = render :partial => "notes/show", :locals => {:note => note} + +%br +%br +- if can? current_user, :write_note, @project + = render "notes/form" + +:javascript + $('.delete-note').live('ajax:success', function() { + $(this).closest('li').fadeOut(); }); + + diff --git a/app/views/notes/_show.html.haml b/app/views/notes/_show.html.haml new file mode 100644 index 00000000000..2b0a6d2ebcd --- /dev/null +++ b/app/views/notes/_show.html.haml @@ -0,0 +1,19 @@ +%li{:id => dom_id(note)} + %div.note_author + = image_tag gravatar_icon(note.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" + %div.note_content + = simple_format(html_escape(note.note)) + - if note.attachment.url + Attachment: + = link_to note.attachment_identifier, note.attachment.url + %br + %span + %span + [ #{note.author.name} ] +   + = time_ago_in_words(note.updated_at) + ago + %br + - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) + = link_to 'Remove', [@project, note], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-note right negative" + .clear diff --git a/app/views/notes/create.js.haml b/app/views/notes/create.js.haml new file mode 100644 index 00000000000..47cff1d8834 --- /dev/null +++ b/app/views/notes/create.js.haml @@ -0,0 +1,8 @@ +- if @note.valid? + :plain + $("#new_note .errors").remove(); + $("#notes-list").append("#{escape_javascript(render(:partial => 'show', :locals => {:note => @note} ))}"); + $('#note_note').val(""); +- else + :plain + $("#new_note").replaceWith("#{escape_javascript(render('form'))}"); diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml new file mode 100644 index 00000000000..213a73796cf --- /dev/null +++ b/app/views/notify/new_issue_email.html.haml @@ -0,0 +1,18 @@ +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"} + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + Hi #{@user.name}! New Issue was created and assigned to you. + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + = link_to project_issue_url(@project, @issue) do + = "Issue ##{@issue.id.to_s}" + = truncate(@issue.title, :length => 45) + %br + %cite{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = @issue.content diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml new file mode 100644 index 00000000000..969ea7e5298 --- /dev/null +++ b/app/views/notify/new_user_email.html.haml @@ -0,0 +1,23 @@ +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"} + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + Hi #{@user.name}! + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + Administrator created account for you. Now you are a member of company gitlab application. + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:style => "padding: 15px 0 15px;", :valign => "top"} + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 16px;font-family: Helvetica, Arial, sans-serif; "} + login.......................................... + %code= @user.email + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 16px;font-family: Helvetica, Arial, sans-serif; "} + password.................................. + %code= @password + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 28px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = link_to "Click here to login", root_url + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml new file mode 100644 index 00000000000..09ae54ac629 --- /dev/null +++ b/app/views/notify/note_commit_email.html.haml @@ -0,0 +1,23 @@ +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"} + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New comment for commmit + = link_to truncate(@commit.id.to_s, :length => 16), project_commit_url(@project, :id => @commit.id) + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:style => "padding: 15px 0 15px;", :valign => "top"} + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{:href => "#", :style => "color: #0eb6ce; text-decoration: none;"} #{@note.author.name} + left next message: + %br + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :width => "558"} + %tr + %td{:valign => "top"} + %cite{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = @note.note + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + diff --git a/app/views/notify/note_issue_email.html.haml b/app/views/notify/note_issue_email.html.haml new file mode 100644 index 00000000000..54982af6286 --- /dev/null +++ b/app/views/notify/note_issue_email.html.haml @@ -0,0 +1,25 @@ +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"} + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New comment - + = link_to project_issue_url(@project, @issue) do + = "Issue ##{@issue.id.to_s}" + = truncate(@issue.title, :length => 35) + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:style => "padding: 15px 0 15px;", :valign => "top"} + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{:href => "#", :style => "color: #0eb6ce; text-decoration: none;"} #{@note.author.name} + left next message: + %br + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :width => "558"} + %tr + %td{:valign => "top"} + %cite{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = @note.note + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + diff --git a/app/views/notify/note_wall_email.html.haml b/app/views/notify/note_wall_email.html.haml new file mode 100644 index 00000000000..285fc763613 --- /dev/null +++ b/app/views/notify/note_wall_email.html.haml @@ -0,0 +1,22 @@ +%td.content{:align => "left", :style => "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", :valign => "top", :width => "600"} + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :style => "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", :width => "600"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:align => "left", :style => "padding: 20px 0 0;"} + %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} + New message on + = link_to "Project Wall", wall_project_url(@project) + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %tr + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} + %td{:style => "padding: 15px 0 15px;", :valign => "top"} + %p{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + %a{:href => "#", :style => "color: #0eb6ce; text-decoration: none;"} #{@note.author.name} + left next message: + %br + %table{:border => "0", :cellpadding => "0", :cellspacing => "0", :width => "558"} + %tr + %td{:valign => "top"} + %cite{:style => "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} + = @note.note + %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} diff --git a/app/views/profile/_top_menu.html.haml b/app/views/profile/_top_menu.html.haml new file mode 100644 index 00000000000..6bf3b09f499 --- /dev/null +++ b/app/views/profile/_top_menu.html.haml @@ -0,0 +1,5 @@ +%div.top_project_menu + %span= link_to "Profile", profile_path, :class => current_page?(:controller => "profile", :action => :show) ? "current" : nil + %span= link_to "Password", profile_password_path, :style => "width:70px;", :class => current_page?(:controller => "profile", :action => :password) ? "current" : nil + %span= link_to "Keys", keys_path, :class => controller.controller_name == "keys" ? "current" : nil + diff --git a/app/views/profile/index.html.haml b/app/views/profile/index.html.haml new file mode 100644 index 00000000000..84174ac5be7 --- /dev/null +++ b/app/views/profile/index.html.haml @@ -0,0 +1 @@ +%h1 Profile diff --git a/app/views/profile/password.html.haml b/app/views/profile/password.html.haml new file mode 100644 index 00000000000..f77d3855364 --- /dev/null +++ b/app/views/profile/password.html.haml @@ -0,0 +1,20 @@ +%p Note: after success password update you will be redirected to login page where you should login with new password += form_for @user, :url => profile_password_path, :method => :put do |f| + -if @user.errors.any? + #error_explanation + %h2= "#{pluralize(@user.errors.count, "error")} prohibited this password from being saved:" + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .div + = f.label :password + %br + = f.password_field :password + .div + = f.label :password_confirmation + %br + = f.password_field :password_confirmation + .actions + = f.submit 'Save', :class => "lbutton vm" + diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml new file mode 100644 index 00000000000..12737ba8143 --- /dev/null +++ b/app/views/profile/show.html.haml @@ -0,0 +1,8 @@ +.span-2 + = image_tag gravatar_icon(@user.email), :class => "left", :width => 60, :style => "padding-right:5px;" +%p + %b Name: + = @user.name +%p + %b Email: + = @user.email diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml new file mode 100644 index 00000000000..baa1f14fe06 --- /dev/null +++ b/app/views/projects/_form.html.haml @@ -0,0 +1,50 @@ += form_for(@project, :remote => true) do |f| + %div.form_content + - if @project.new_record? + %h1 New Project + - else + %h1 Edit Project + - if @project.errors.any? + #error_explanation + %h2 + = pluralize(@project.errors.count, "error") + prohibited this project from being saved: + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + %table.round-borders + %tr + %td= f.label :name + %td= f.text_field :name, :placeholder => "Example Project" + %tr + %td + .left= f.label :path + %cite.right git@yourserver: + %td + = f.text_field :path, :placeholder => "example_project", :disabled => !@project.new_record? + %tr + %td + .left= f.label :code + %cite.right http://yourserver/ + %td= f.text_field :code, :placeholder => "example (3..12 symbols only)" + .field + = f.label :description + %br/ + = f.text_area :description, :style => "height:140px;width:932px;" + .clear + %hr.prepend-top + .actions + = f.submit :class => "lbutton vm" + + %div{ :class => "ajax_loader", :style => "display:none;height:200px;"} + %center + = image_tag "ajax-loader.gif", :class => "append-bottom" + - if @project.new_record? + %h3.prepend-top Creating project & repository. Please wait for few minutes + - else + %h3.prepend-top Updating project & repository. Please wait for few minutes +:javascript + $('.new_project, .edit_project').bind('ajax:before', function() { + $(this).find(".form_content").hide(); + $('.ajax_loader').show(); + }); diff --git a/app/views/projects/_list.html.haml b/app/views/projects/_list.html.haml new file mode 100644 index 00000000000..82e1948c757 --- /dev/null +++ b/app/views/projects/_list.html.haml @@ -0,0 +1,26 @@ +-#- if current_user.can_create_project? + = link_to 'New Project', new_project_path, :class => "lbutton vm" + +%table.round-borders#projects-list + %tr + %th Name + %th Path + %th Code + %th Web + %th Git + %th Admin + %th Actions + + - @projects.each do |project| + %tr{ :class => "project", :url => project_path(project) } + %td= project.name + %td= truncate project.url_to_repo + %td= project.code + %td= check_box_tag "read", 1, project.readers.include?(current_user), :disabled => :disabled + %td= check_box_tag "commit", 1, project.writers.include?(current_user), :disabled => :disabled + %td= check_box_tag "admin", 1, project.admins.include?(current_user), :disabled => :disabled + %td + -if can? current_user, :admin_project, project + = link_to 'Edit', edit_project_path(project), :class => "lbutton positive" +%br + diff --git a/app/views/projects/_projects_top_menu.html.haml b/app/views/projects/_projects_top_menu.html.haml new file mode 100644 index 00000000000..be04c57e842 --- /dev/null +++ b/app/views/projects/_projects_top_menu.html.haml @@ -0,0 +1,16 @@ +%div.top_project_menu + %span= link_to 'All', projects_path, :class => current_page?(projects_path) ? "current" : nil + %span= link_to "New Project", new_project_path, :class => current_page?(:controller => "projects", :action => "new") ? "current" : nil + %span.right + = link_to_function(image_tag("list_view_icon.jpg"), "switchProjectView()", :style => "border:none;box-shadow:none;") + +:javascript + function switchProjectView(){ + $(".tile").toggle(); + $(".list").toggle(); + if($(".tile").is(":visible")){ + $.cookie('project_view', 'tile', { expires: 14 }); + } else { + $.cookie('project_view', 'list', { expires: 14 }); + } + } diff --git a/app/views/projects/_side_panel.html.haml b/app/views/projects/_side_panel.html.haml new file mode 100644 index 00000000000..2f786b4d242 --- /dev/null +++ b/app/views/projects/_side_panel.html.haml @@ -0,0 +1,14 @@ +%h3.notice{:style => "width:235px;"} + = @project.name +%p + %b Path: + = @project.path +%p + %b Description: + = truncate @project.description +.left.append-bottom + = link_to "Tree", tree_project_path(@project), :class => "button" + = link_to "Commits", project_commits_path(@project), :class => "button" + = link_to 'Team', team_project_path(@project), :class => "button" + - if can? current_user, :admin_project, @project + = link_to 'Edit', edit_project_path(@project), :class => "button positive" diff --git a/app/views/projects/_team.html.haml b/app/views/projects/_team.html.haml new file mode 100644 index 00000000000..bb9063369c3 --- /dev/null +++ b/app/views/projects/_team.html.haml @@ -0,0 +1,18 @@ +- if can? current_user, :admin_team_member, @project + %div#new-member-holder + = link_to "Add new", new_project_team_member_path(@project), :remote => true, :class => "lbutton vm" +%table.round-borders#team-table + %tr + %th Name + %th Email + %th Web + %th Git + %th Admin + - if can? current_user, :admin_team_member, @project + %th Actions + - @project.users_projects.each do |up| + = render(:partial => 'team_members/show', :locals => {:member => up}) + +:javascript + $('.delete-team-member').live('ajax:success', function() { + $(this).closest('tr').fadeOut(); }); diff --git a/app/views/projects/_tile.html.haml b/app/views/projects/_tile.html.haml new file mode 100644 index 00000000000..037aeccb228 --- /dev/null +++ b/app/views/projects/_tile.html.haml @@ -0,0 +1,16 @@ +- @projects.in_groups_of(3, false) do |projects| + - projects.each_with_index do |project, i| + %div{ :class => "project_thumb round-borders", :style => i == 2 ? "" : "margin-right:30px;" } + %div{ :class => "project", :url => project_path(project) } + %h2 + = image_tag gravatar_icon(project.name), :class => "left", :width => 40, :style => "padding-right:5px;" + = "/" + project.code + %p= project.name + %p= project.url_to_repo + -#%p + Commit – + = last_commit(project) + %hr + = link_to "Browse Code", tree_project_path(project), :class => "lbutton" + = link_to "Commits", project_commits_path(project), :class => "lbutton", :style => "float:right;width:80px;" + .clear diff --git a/app/views/projects/_top_menu.html.haml b/app/views/projects/_top_menu.html.haml new file mode 100644 index 00000000000..b81ba6bb4d6 --- /dev/null +++ b/app/views/projects/_top_menu.html.haml @@ -0,0 +1,24 @@ +%div.top_project_menu + -#%span= link_to @project.code.capitalize, @project, :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil + - if @project.repo_exists? + %span= link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) || current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil + %span= link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil + %span + = link_to team_project_path(@project), :class => current_page?(:controller => "projects", :action => "team", :id => @project) ? "current" : nil do + Team + - if @project.users_projects.count > 0 + %span{ :class => "top_menu_count" }= @project.users_projects.count + %span + = link_to project_issues_path(@project), :class => (controller.controller_name == "issues") ? "current" : nil do + Issues + - if @project.issues.opened.count > 0 + %span{ :class => "top_menu_count" }= @project.issues.opened.count + %span + = link_to wall_project_path(@project), :class => current_page?(:controller => "projects", :action => "wall", :id => @project) ? "current" : nil do + Wall + - if @project.common_notes.count > 0 + %span{ :class => "top_menu_count" }= @project.common_notes.count + + - if @commit + %span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil + diff --git a/app/views/projects/_tree.html.haml b/app/views/projects/_tree.html.haml new file mode 100644 index 00000000000..af3a209a8ab --- /dev/null +++ b/app/views/projects/_tree.html.haml @@ -0,0 +1,60 @@ +%h3 + .left + = form_tag tree_project_path(@project), :method => :get do + = select_tag "branch", options_for_select(@repo.heads.map(&:name), @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Branches" +   + .left.prepend-1 + = form_tag tree_project_path(@project), :method => :get do + = select_tag "tag", options_for_select(@project.tags, @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" + = text_field_tag "ssh", @project.url_to_repo, :class => ["ssh_project_url","one_click_select"] + .clear + +%h3#tree-breadcrumbs + = link_to @project.name, tree_project_path(@project, :path => nil, :commit_id => @commit.try(:id)), :remote => true + - if params[:path] + - part_path = "" + - params[:path].split("\/").each do |part| + - part_path = File.join(part_path, part) unless part_path.empty? + - if part_path.empty? + - part_path = part + \/ + = link_to truncate(part, :length => 40), tree_file_project_path(@project, :path => part_path, :commit_id => @commit.try(:id)), :remote => :true +#tree-content-holder + - if tree.is_a?(Grit::Blob) + = render :partial => "projects/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree } + - else + - contents = tree.contents + %table#tree-slider.round-borders + %tr + %th Name + %th Last Update + %th + Last commit + = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right" + - if params[:path] + - file = File.join(params[:path], "..") + %tr{ :class => "tree-item", :url => tree_file_project_path(@project, @commit.id, file) } + %td.tree-item-file-name + = image_tag "dir.png" + = link_to "..", tree_file_project_path(@project, @commit.id, file), :remote => :true + %td + %td + + - contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content| + = render :partial => "projects/tree_item", :locals => { :content => content } + - contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content| + = render :partial => "projects/tree_item", :locals => { :content => content } + +:javascript + $(function(){ + $('select#branch').selectmenu({style:'popup', width:200}); + $('select#tag').selectmenu({style:'popup', width:200}); + }); + +- if params[:path] && request.xhr? + :javascript + $(window).unbind('popstate'); + $(window).bind('popstate', function() { + if(location.pathname.search("tree") != -1) { + $.ajax({type: "GET", url: location.pathname, dataType: "script"})} + else { location.href = location.pathname;}}); diff --git a/app/views/projects/_tree_file.html.haml b/app/views/projects/_tree_file.html.haml new file mode 100644 index 00000000000..08927fc2aac --- /dev/null +++ b/app/views/projects/_tree_file.html.haml @@ -0,0 +1,21 @@ +- require "utils" +.view_file + .view_file_header + %strong + = name + -#= file.mime_type + = link_to "raw", blob_project_path(@project, :commit_id => @commit.id, :path => params[:path] ), :class => "right", :target => "_blank" + = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right", :style => "margin-right:10px;" + %br/ + - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + .view_file_content + - ft = handle_file_type(file.name, file.mime_type) + :erb + <%= raw Albino.colorize(content, ft, :html, 'utf-8', "linenos=True") %> + - elsif file.mime_type =~ /image/ + .view_file_content_image + %img{ :src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + - else + %p + %center No preview for this file type + diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml new file mode 100644 index 00000000000..7179368a0c3 --- /dev/null +++ b/app/views/projects/_tree_item.html.haml @@ -0,0 +1,15 @@ +- file = params[:path] ? File.join(params[:path], content.name) : content.name +- content_commit = @project.repo.log(@branch, file, :max_count => 1).last +- return unless content_commit +%tr{ :class => "tree-item", :url => tree_file_project_path(@project, @commit.id, file) } + %td.tree-item-file-name + - if content.is_a?(Grit::Blob) + = image_tag "txt.png" + - else + = image_tag "dir.png" + = link_to truncate(content.name, :length => 40), tree_file_project_path(@project, @commit.id, file), :remote => :true + %td + = time_ago_in_words(content_commit.committed_date) + ago + %td + = link_to truncate(content_commit.message, :length => 40), project_commit_path(@project, content_commit) diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml new file mode 100644 index 00000000000..c457527aed4 --- /dev/null +++ b/app/views/projects/create.js.haml @@ -0,0 +1,6 @@ +- if @project.valid? + :plain + location.href = "#{project_path(@project, :notice => 'Project was successfully created.')}"; +- else + :plain + $("#new_project").replaceWith("#{escape_javascript(render('form'))}"); diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb new file mode 100644 index 00000000000..2d3436368bd --- /dev/null +++ b/app/views/projects/edit.html.erb @@ -0,0 +1 @@ +<%= render 'form' %> diff --git a/app/views/projects/empty.html.erb b/app/views/projects/empty.html.erb new file mode 100644 index 00000000000..a8917471550 --- /dev/null +++ b/app/views/projects/empty.html.erb @@ -0,0 +1,49 @@ +
      +
      +

      Git global setup:

      +<% setup_str = < + <%= raw Albino.colorize(setup_str, :bash) %> +
      +
      +

      Next steps:

      +<% repo_setup_str = < + <%= raw Albino.colorize(repo_setup_str, :bash) %> + +

      +

      Existing Git Repo?

      +<% exist_repo_setup_str = < + <%= raw Albino.colorize(exist_repo_setup_str, :bash) %> + +

      +

      Remove this project?

      +
      +

      + Be careful!
      + Project cant be recovered after destroy.

      + <%= link_to 'Destroy', @project, + :confirm => 'Are you sure?', :method => :delete, + :class => "left button negative span-6", :style => "text-align:center" %> +
      +
      +
      +
      +
      diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml new file mode 100644 index 00000000000..22af6b7551a --- /dev/null +++ b/app/views/projects/index.html.haml @@ -0,0 +1,4 @@ +%div{:class => "tile", :style => view_mode_style("tile")} + = render "tile" +%div{:class => "list", :style => view_mode_style("list")} + = render "list" diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb new file mode 100644 index 00000000000..2d3436368bd --- /dev/null +++ b/app/views/projects/new.html.erb @@ -0,0 +1 @@ +<%= render 'form' %> diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml new file mode 100644 index 00000000000..0fd9c8ead19 --- /dev/null +++ b/app/views/projects/show.html.haml @@ -0,0 +1,3 @@ +%div + %div#tree-holder + = render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @commit.tree} diff --git a/app/views/projects/team.html.haml b/app/views/projects/team.html.haml new file mode 100644 index 00000000000..3b2c4b3ef9c --- /dev/null +++ b/app/views/projects/team.html.haml @@ -0,0 +1,3 @@ +%div + = render :partial => "team", :locals => {:project => @project} + diff --git a/app/views/projects/tree.html.erb b/app/views/projects/tree.html.erb new file mode 100644 index 00000000000..c29ed2f90d4 --- /dev/null +++ b/app/views/projects/tree.html.erb @@ -0,0 +1,5 @@ +
      +
      + <%= render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree} %> +
      +
      diff --git a/app/views/projects/tree.js.haml b/app/views/projects/tree.js.haml new file mode 100644 index 00000000000..60cbd199165 --- /dev/null +++ b/app/views/projects/tree.js.haml @@ -0,0 +1,5 @@ +:plain + $("#tree-holder table").hide("slide", { direction: "left" }, 150, function(){ + $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}"); + $("#tree-holder table").show("slide", { direction: "right" }, 150); + }); diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml new file mode 100644 index 00000000000..0188f083735 --- /dev/null +++ b/app/views/projects/update.js.haml @@ -0,0 +1,6 @@ +- if @project.valid? + :plain + location.href = "#{project_path(@project, :notice => 'Project was successfully updated.')}"; +- else + :plain + $(".edit_project").replaceWith("#{escape_javascript(render('form'))}"); diff --git a/app/views/projects/wall.html.haml b/app/views/projects/wall.html.haml new file mode 100644 index 00000000000..479bb3cfc5b --- /dev/null +++ b/app/views/projects/wall.html.haml @@ -0,0 +1 @@ += render "notes/notes" diff --git a/app/views/team_members/_form.html.haml b/app/views/team_members/_form.html.haml new file mode 100644 index 00000000000..d2f7fd66dce --- /dev/null +++ b/app/views/team_members/_form.html.haml @@ -0,0 +1,25 @@ +%div + = form_for @team_member, :as => :team_member, :url => project_team_members_path(@project, @team_member), :remote => "true" do |f| + -if @team_member.errors.any? + %ul + - @team_member.errors.full_messages.each do |msg| + %li= msg + + .span-6.append-bottom + %b Name + %br + = f.select(:user_id, User.not_in_project(@project).all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }) + .span-6 + %b Access: + .span-6 + = f.check_box :read + Web Access + .span-6 + = f.check_box :write + Git Access + .span-6.append-bottom + = f.check_box :admin + Admin + %hr + .span-6 + = f.submit 'Save', :class => "lbutton vm" diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml new file mode 100644 index 00000000000..6d310768afa --- /dev/null +++ b/app/views/team_members/_show.html.haml @@ -0,0 +1,18 @@ +- user = member.user +%tr{:id => dom_id(member)} + %td + = image_tag gravatar_icon(user.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = truncate user.name, :lenght => 16 + %td= truncate user.email, :lenght => 16 + - if can? current_user, :admin_project, @project + = form_for(member, :as => :team_member, :url => project_team_member_path(@project, member)) do |f| + %td= f.check_box :read, :onclick => "$(this.form).submit();" + %td= f.check_box :write, :onclick => "$(this.form).submit();" + %td= f.check_box :admin, :onclick => "$(this.form).submit();" + - else + %td= check_box_tag "read", 1, member.read, :disabled => :disabled + %td= check_box_tag "commit", 1, member.write, :disabled => :disabled + %td= check_box_tag "admin", 1, member.admin, :disabled => :disabled + - if can? current_user, :admin_team_member, @project + %td + = link_to 'Cancel', project_team_member_path(:project_id => @project, :id => member.id), :confirm => 'Are you sure?', :method => :delete, :class => "lbutton negative delete-team-member", :remote => true diff --git a/app/views/team_members/create.js.haml b/app/views/team_members/create.js.haml new file mode 100644 index 00000000000..74dacc2820c --- /dev/null +++ b/app/views/team_members/create.js.haml @@ -0,0 +1,9 @@ +- if @team_member.valid? + :plain + $("#new_tm_dialog").dialog("close"); + $("#team-table").append("#{escape_javascript(render(:partial => 'show', :locals => {:member => @team_member} ))}"); +- else + :plain + $("#new_tm_dialog").empty(); + $("#new_tm_dialog").append("#{escape_javascript(render('form'))}"); + $('select#team_member_user_id').selectmenu({width:300}); diff --git a/app/views/team_members/new.js.haml b/app/views/team_members/new.js.haml new file mode 100644 index 00000000000..93bebba6d5e --- /dev/null +++ b/app/views/team_members/new.js.haml @@ -0,0 +1,15 @@ +-#$("#new-member-holder").empty(); +-#$("#new-member-holder").append("#{escape_javascript(render('form'))}"); +:plain + var new_tm_dialog = $("
      "); + new_tm_dialog.html("#{escape_javascript(render('form'))}"); + $(new_tm_dialog).dialog({ + width: 350, + resizable: false, + draggable: false, + title: "Add new member to project team", + close: function(event, ui) { $("#new_tm_dialog").remove();}, + modal: true + + }); + $('select#team_member_user_id').selectmenu({width:300}); diff --git a/app/views/team_members/update.js.haml b/app/views/team_members/update.js.haml new file mode 100644 index 00000000000..6d7f88160de --- /dev/null +++ b/app/views/team_members/update.js.haml @@ -0,0 +1,6 @@ +- if @team_member.valid? + :plain + $("##{dom_id(@team_member)}").effect("highlight", {color: "#529214"}, 1000);; +- else + :plain + $("##{dom_id(@team_member)}").effect("highlight", {color: "#D12F19"}, 1000);; diff --git a/config.ru b/config.ru new file mode 100644 index 00000000000..5ef2a0289fe --- /dev/null +++ b/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Gitlab::Application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 00000000000..9a4fd4e4586 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,48 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' + +if defined?(Bundler) + # If you precompile assets before deploying to production, use this line + Bundler.require *Rails.groups(:assets => %w(development test)) + # If you want your assets lazily compiled in production, use this line + # Bundler.require(:default, :assets, Rails.env) +end + +module Gitlab + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Custom directories with classes and modules you want to be autoloadable. + config.autoload_paths += %W(#{config.root}/lib) + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named. + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + # Activate observers that should always be running. + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # config.time_zone = 'Central Time (US & Canada)' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + + # Configure the default encoding used in templates for Ruby 1.9. + config.encoding = "utf-8" + + # Configure sensitive parameters which will be filtered from the log file. + config.filter_parameters += [:password] + + # Enable the asset pipeline + config.assets.enabled = true + + # Version of your assets, change this if you want to expire all your assets + config.assets.version = '1.0' + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 00000000000..4489e58688c --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,6 @@ +require 'rubygems' + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 00000000000..51a4dd459dc --- /dev/null +++ b/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 00000000000..1c2d723e18b --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,7 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +Gitlab::Application.initialize! + +require File.join(Rails.root, "lib", "gitosis") diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 00000000000..9e5bf8215e7 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,32 @@ +Gitlab::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Don't care if the mailer can't send + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger + config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin + + # Do not compress assets + config.assets.compress = false + + # Expands the lines which load the assets + config.assets.debug = true + + config.action_mailer.default_url_options = { :host => 'localhost:3000' } +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 00000000000..7ebe4523967 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,70 @@ +Gitlab::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable Rails's static asset server (Apache or nginx will already do this) + config.serve_static_assets = false + + # Compress JavaScripts and CSS + config.assets.compress = true + + # Don't fallback to assets pipeline if a precompiled asset is missed + config.assets.compile = true + + # Generate digests for assets URLs + config.assets.digest = true + + # Defaults to Rails.root.join("public/assets") + # config.assets.manifest = YOUR_PATH + + # Specifies the header that your server uses for sending files + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Enable serving of images, stylesheets, and JavaScripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + # config.assets.precompile += %w( search.js ) + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + + # Enable threaded mode + # config.threadsafe! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify + + + config.action_mailer.delivery_method = :sendmail + # Defaults to: + # # config.action_mailer.sendmail_settings = { + # # :location => '/usr/sbin/sendmail', + # # :arguments => '-i -t' + # # } + config.action_mailer.perform_deliveries = true + config.action_mailer.raise_delivery_errors = true +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 00000000000..1e7765d9719 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,42 @@ +Gitlab::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Configure static asset server for tests with Cache-Control for performance + config.serve_static_assets = true + config.static_cache_control = "public, max-age=3600" + + # Log error messages when you accidentally call methods on nil + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment + config.action_controller.allow_forgery_protection = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Use SQL instead of Active Record's schema dumper when creating the test database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql + + # Print deprecation notices to the stderr + config.active_support.deprecation = :stderr + + # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets + config.assets.allow_debugging = true +end diff --git a/config/gitosis.yml b/config/gitosis.yml new file mode 100644 index 00000000000..cf76f5a2f7e --- /dev/null +++ b/config/gitosis.yml @@ -0,0 +1,4 @@ +admin_uri: git@localhost:gitosis-admin.git +base_path: /home/git/repositories/ +host: localhost +git_user: git diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 00000000000..59385cdf379 --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 00000000000..e62db747a64 --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,211 @@ +# Use this hook to configure devise mailer, warden hooks and so forth. The first +# four configuration values can also be set straight in your models. +Devise.setup do |config| + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class with default "from" parameter. + config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + + # Configure the class responsible to send e-mails. + # config.mailer = "Devise::Mailer" + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [ :email ] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [ :email ] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [ :email ] + + # Tell if authentication through request.params is enabled. True by default. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Basic Auth is enabled. False by default. + # config.http_authenticatable = false + + # If http headers should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. "Application" by default. + # config.http_authentication_realm = "Application" + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 10. If + # using other encryptors, it sets how many times you want the password re-encrypted. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. + config.stretches = Rails.env.test? ? 1 : 10 + + # Setup a pepper to generate the encrypted password. + # config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef" + + # ==> Configuration for :confirmable + # The time you want to give your user to confirm his account. During this time + # he will be able to access your application without confirming. Default is 0.days + # When confirm_within is zero, the user won't be able to sign in without confirming. + # You can use this to let your user access some features of your application + # without confirming the account, but blocking it after a certain period + # (ie 2 days). + # config.confirm_within = 2.days + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [ :email ] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # If true, a valid remember token can be re-used between multiple browsers. + # config.remember_across_browsers = true + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # If true, uses the password salt as remember token. This should be turned + # to false if you are not using database authenticatable. + config.use_salt_as_remember_token = true + + # Options to be passed to the created cookie. For instance, you can set + # :secure => true in order to force SSL only cookies. + # config.cookie_options = {} + + # ==> Configuration for :validatable + # Range for password length. Default is 6..128. + # config.password_length = 6..128 + + # Email regex used to validate email formats. It simply asserts that + # an one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + # config.email_regexp = /\A[^@]+@[^@]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [ :email ] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [ :email ] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 2.hours + + # ==> Configuration for :encryptable + # Allow you to use another encryption algorithm besides bcrypt (default). You can use + # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, + # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) + # and :restful_authentication_sha1 (then you should set stretches to 10, and copy + # REST_AUTH_SITE_KEY to pepper) + # config.encryptor = :sha512 + + # ==> Configuration for :token_authenticatable + # Defines name of the authentication token params key + # config.token_authentication_key = :auth_token + + # If true, authentication through token does not store user in session and needs + # to be supplied on each request. Useful if you are using the token as API token. + # config.stateless_token = false + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Configure sign_out behavior. + # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope). + # The default is true, which means any logout action will sign out all active scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The :"*/*" and "*/*" formats below is required to match Internet + # Explorer requests. + # config.navigational_formats = [:"*/*", "*/*", :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.failure_app = AnotherApp + # manager.intercept_401 = false + # manager.default_strategies(:scope => :user).unshift :some_external_strategy + # end +end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 00000000000..9e8b0131f8f --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/config/initializers/load_config.rb b/config/initializers/load_config.rb new file mode 100644 index 00000000000..6c7289e0380 --- /dev/null +++ b/config/initializers/load_config.rb @@ -0,0 +1 @@ +GITOSIS = YAML.load_file("#{Rails.root}/config/gitosis.yml") diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb new file mode 100644 index 00000000000..72aca7e441e --- /dev/null +++ b/config/initializers/mime_types.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone diff --git a/config/initializers/rails_footnotes.rb b/config/initializers/rails_footnotes.rb new file mode 100644 index 00000000000..da9d58e4acb --- /dev/null +++ b/config/initializers/rails_footnotes.rb @@ -0,0 +1,5 @@ +if defined?(Footnotes) && Rails.env.development? + Footnotes.run! # first of all + + # ... other init code +end diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb new file mode 100644 index 00000000000..6d3a9f07787 --- /dev/null +++ b/config/initializers/secret_token.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +Gitlab::Application.config.secret_token = '0a38e9a40ca5d66d7002a6ade0ed0f8b71058c820163f66cf65d91521ab55255ff708b9909b138008a7f13d68fec575def1dc3ff7200cd72b065896315e0bed2' diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 00000000000..36c5f4666a7 --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +Gitlab::Application.config.session_store :cookie_store, key: '_gitlab_session' + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rails generate session_migration") +# Gitlab::Application.config.session_store :active_record_store diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 00000000000..999df20181e --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# Disable root element in JSON by default. +ActiveSupport.on_load(:active_record) do + self.include_root_in_json = false +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 00000000000..b18263510f8 --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,58 @@ +# Additional translations at http://github.com/plataformatec/devise/wiki/I18n + +en: + errors: + messages: + expired: "has expired, please request a new one" + not_found: "not found" + already_confirmed: "was already confirmed, please try signing in" + not_locked: "was not locked" + not_saved: + one: "1 error prohibited this %{resource} from being saved:" + other: "%{count} errors prohibited this %{resource} from being saved:" + + devise: + failure: + already_authenticated: 'You are already signed in.' + unauthenticated: 'You need to sign in or sign up before continuing.' + unconfirmed: 'You have to confirm your account before continuing.' + locked: 'Your account is locked.' + invalid: 'Invalid email or password.' + invalid_token: 'Invalid authentication token.' + timeout: 'Your session expired, please sign in again to continue.' + inactive: 'Your account was not activated yet.' + sessions: + signed_in: 'Signed in successfully.' + signed_out: 'Signed out successfully.' + passwords: + send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' + updated: 'Your password was changed successfully. You are now signed in.' + updated_not_active: 'Your password was changed successfully.' + send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail" + confirmations: + send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' + send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.' + confirmed: 'Your account was successfully confirmed. You are now signed in.' + registrations: + signed_up: 'Welcome! You have signed up successfully.' + inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.' + updated: 'You updated your account successfully.' + destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' + reasons: + inactive: 'inactive' + unconfirmed: 'unconfirmed' + locked: 'locked' + unlocks: + send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' + unlocked: 'Your account was successfully unlocked. You are now signed in.' + send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.' + omniauth_callbacks: + success: 'Successfully authorized from %{kind} account.' + failure: 'Could not authorize you from %{kind} because "%{reason}".' + mailer: + confirmation_instructions: + subject: 'Confirmation instructions' + reset_password_instructions: + subject: 'Reset password instructions' + unlock_instructions: + subject: 'Unlock Instructions' diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 00000000000..f6cfb5efd2a --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,10 @@ +# Sample localization file for English. Add more files in this directory for other locales. +# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + hello: "Hello world" + errors: + messages: + wrong_size: "is the wrong size (should be %{file_size})" + size_too_small: "is too small (should be at least %{file_size})" + size_too_big: "is too big (should be at most %{file_size})" diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 00000000000..00106b119a6 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,47 @@ +Gitlab::Application.routes.draw do + namespace :admin do + resources :users + resources :projects + resources :team_members + get 'emails', :to => 'mailer#preview' + get 'mailer/preview_note' + get 'mailer/preview_user_new' + get 'mailer/preview_issue_new' + root :to => "users#index" + end + + get "errors/gitosis" + get "profile/password", :to => "profile#password" + put "profile/password", :to => "profile#password_update" + get "profile", :to => "profile#show" + #get "profile/:id", :to => "profile#show" + + resources :projects, :only => [:new, :create, :index] + resources :keys + devise_for :users + + resources :projects, :except => [:new, :create, :index], :path => "/" do + member do + get "tree" + get "blob" + get "team" + get "wall" + + # tree viewer + get "tree/:commit_id" => "projects#tree" + get "tree/:commit_id/:path" => "projects#tree", + :as => :tree_file, + :constraints => { + :id => /[a-zA-Z0-9]+/, + :commit_id => /[a-zA-Z0-9]+/, + :path => /.*/ + } + + end + resources :commits + resources :team_members + resources :issues + resources :notes, :only => [:create, :destroy] + end + root :to => "projects#index" +end diff --git a/configure.rb b/configure.rb new file mode 100644 index 00000000000..27bad806ff6 --- /dev/null +++ b/configure.rb @@ -0,0 +1,5 @@ +root_path = File.expand_path(File.dirname(__FILE__)) +require File.join(root_path, "install", "prepare") +env = ARGV[0] || "development" + +Install.prepare(env) diff --git a/db/fixtures/development/001_admin.rb b/db/fixtures/development/001_admin.rb new file mode 100644 index 00000000000..9c5a6da09e0 --- /dev/null +++ b/db/fixtures/development/001_admin.rb @@ -0,0 +1,11 @@ +# Admin account +admin = User.create( + :email => "admin@local.host", + :name => "Administrator", + :password => "5iveL!fe", + :password_confirmation => "5iveL!fe" +) + +admin.projects_limit = 10000 +admin.admin = true +admin.save! diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb new file mode 100644 index 00000000000..94d1abe87ce --- /dev/null +++ b/db/fixtures/production/001_admin.rb @@ -0,0 +1,10 @@ +admin = User.create( + :email => "admin@local.host", + :name => "Administrator", + :password => "5iveL!fe", + :password_confirmation => "5iveL!fe" +) + +admin.projects_limit = 10000 +admin.admin = true +admin.save! diff --git a/db/fixtures/test/001_repo.rb b/db/fixtures/test/001_repo.rb new file mode 100644 index 00000000000..3eb4c299b4a --- /dev/null +++ b/db/fixtures/test/001_repo.rb @@ -0,0 +1,11 @@ +# Create dir for test repo +repo_dir = File.join(Rails.root, "tmp", "tests") +Dir.mkdir(repo_dir) unless File.exists?(repo_dir) + +`cp spec/seed_project.tar.gz tmp/tests/` +Dir.chdir(repo_dir) +`tar -xf seed_project.tar.gz` +3.times do |i| +`cp -r legit/ legit_#{i}/` +puts "Unpacked seed repo - tmp/tests/legit_#{i}" +end diff --git a/db/migrate/20110913200833_devise_create_users.rb b/db/migrate/20110913200833_devise_create_users.rb new file mode 100644 index 00000000000..3083e742459 --- /dev/null +++ b/db/migrate/20110913200833_devise_create_users.rb @@ -0,0 +1,28 @@ +class DeviseCreateUsers < ActiveRecord::Migration + def self.up + create_table(:users) do |t| + t.database_authenticatable :null => false + t.recoverable + t.rememberable + t.trackable + + # t.encryptable + # t.confirmable + # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both + # t.token_authenticatable + + + t.timestamps + end + + add_index :users, :email, :unique => true + add_index :users, :reset_password_token, :unique => true + # add_index :users, :confirmation_token, :unique => true + # add_index :users, :unlock_token, :unique => true + # add_index :users, :authentication_token, :unique => true + end + + def self.down + drop_table :users + end +end diff --git a/db/migrate/20110913204141_create_projects.rb b/db/migrate/20110913204141_create_projects.rb new file mode 100644 index 00000000000..45b76f95563 --- /dev/null +++ b/db/migrate/20110913204141_create_projects.rb @@ -0,0 +1,11 @@ +class CreateProjects < ActiveRecord::Migration + def change + create_table :projects do |t| + t.string :name + t.string :path + t.text :description + + t.timestamps + end + end +end diff --git a/db/migrate/20110914221600_create_users_projects.rb b/db/migrate/20110914221600_create_users_projects.rb new file mode 100644 index 00000000000..a89798ae86f --- /dev/null +++ b/db/migrate/20110914221600_create_users_projects.rb @@ -0,0 +1,13 @@ +class CreateUsersProjects < ActiveRecord::Migration + def change + create_table :users_projects do |t| + t.integer :user_id, :null => false + t.integer :project_id, :null => false + t.boolean :read, :default => false + t.boolean :write, :default => false + t.boolean :admin, :default => false + + t.timestamps + end + end +end diff --git a/db/migrate/20110915205627_add_private_flag_to_project.rb b/db/migrate/20110915205627_add_private_flag_to_project.rb new file mode 100644 index 00000000000..73c0b9df834 --- /dev/null +++ b/db/migrate/20110915205627_add_private_flag_to_project.rb @@ -0,0 +1,5 @@ +class AddPrivateFlagToProject < ActiveRecord::Migration + def change + add_column :projects, :private_flag, :boolean, :default => true, :null => false + end +end diff --git a/db/migrate/20110915213352_create_keys.rb b/db/migrate/20110915213352_create_keys.rb new file mode 100644 index 00000000000..d4615b4babf --- /dev/null +++ b/db/migrate/20110915213352_create_keys.rb @@ -0,0 +1,9 @@ +class CreateKeys < ActiveRecord::Migration + def change + create_table :keys do |t| + t.integer :user_id, :null => false + t.text :project_id, :null => false + t.timestamps + end + end +end diff --git a/db/migrate/20110916123731_add_name_to_user.rb b/db/migrate/20110916123731_add_name_to_user.rb new file mode 100644 index 00000000000..74142b05cb5 --- /dev/null +++ b/db/migrate/20110916123731_add_name_to_user.rb @@ -0,0 +1,5 @@ +class AddNameToUser < ActiveRecord::Migration + def change + add_column :users, :name, :string + end +end diff --git a/db/migrate/20110916162511_add_key_title_to_key.rb b/db/migrate/20110916162511_add_key_title_to_key.rb new file mode 100644 index 00000000000..b2eaa51cccf --- /dev/null +++ b/db/migrate/20110916162511_add_key_title_to_key.rb @@ -0,0 +1,7 @@ +class AddKeyTitleToKey < ActiveRecord::Migration + def change + add_column :keys, :key, :text + add_column :keys, :title, :string + remove_column :keys, :project_id + end +end diff --git a/db/migrate/20110917212932_add_identifier_to_key.rb b/db/migrate/20110917212932_add_identifier_to_key.rb new file mode 100644 index 00000000000..e572793952a --- /dev/null +++ b/db/migrate/20110917212932_add_identifier_to_key.rb @@ -0,0 +1,5 @@ +class AddIdentifierToKey < ActiveRecord::Migration + def change + add_column :keys, :identifier, :string + end +end diff --git a/db/migrate/20110921192501_create_issues.rb b/db/migrate/20110921192501_create_issues.rb new file mode 100644 index 00000000000..63b42ad998e --- /dev/null +++ b/db/migrate/20110921192501_create_issues.rb @@ -0,0 +1,13 @@ +class CreateIssues < ActiveRecord::Migration + def change + create_table :issues do |t| + t.string :title + t.text :content + t.integer :assignee_id + t.integer :author_id + t.integer :project_id + + t.timestamps + end + end +end diff --git a/db/migrate/20110922110156_add_code_to_project.rb b/db/migrate/20110922110156_add_code_to_project.rb new file mode 100644 index 00000000000..f54a02bd70a --- /dev/null +++ b/db/migrate/20110922110156_add_code_to_project.rb @@ -0,0 +1,5 @@ +class AddCodeToProject < ActiveRecord::Migration + def change + add_column :projects, :code, :string + end +end diff --git a/db/migrate/20110923211333_add_status_to_issue.rb b/db/migrate/20110923211333_add_status_to_issue.rb new file mode 100644 index 00000000000..c53bf46ef72 --- /dev/null +++ b/db/migrate/20110923211333_add_status_to_issue.rb @@ -0,0 +1,5 @@ +class AddStatusToIssue < ActiveRecord::Migration + def change + add_column :issues, :closed, :boolean, :default => false, :null => false + end +end diff --git a/db/migrate/20110924214549_create_rails_admin_histories_table.rb b/db/migrate/20110924214549_create_rails_admin_histories_table.rb new file mode 100644 index 00000000000..3c743aa2874 --- /dev/null +++ b/db/migrate/20110924214549_create_rails_admin_histories_table.rb @@ -0,0 +1,18 @@ +class CreateRailsAdminHistoriesTable < ActiveRecord::Migration + def self.up + create_table :rails_admin_histories do |t| + t.text :message # title, name, or object_id + t.string :username + t.integer :item + t.string :table + t.integer :month, :limit => 2 + t.integer :year, :limit => 5 + t.timestamps + end + add_index(:rails_admin_histories, [:item, :table, :month, :year], :name => 'index_rails_admin_histories' ) + end + + def self.down + drop_table :rails_admin_histories + end +end diff --git a/db/migrate/20110924215658_add_admin_field_to_user.rb b/db/migrate/20110924215658_add_admin_field_to_user.rb new file mode 100644 index 00000000000..321587e6957 --- /dev/null +++ b/db/migrate/20110924215658_add_admin_field_to_user.rb @@ -0,0 +1,5 @@ +class AddAdminFieldToUser < ActiveRecord::Migration + def change + add_column :users, :admin, :boolean, :default => false, :null => false + end +end diff --git a/db/migrate/20110926082616_remove_admin.rb b/db/migrate/20110926082616_remove_admin.rb new file mode 100644 index 00000000000..aac9ff7888c --- /dev/null +++ b/db/migrate/20110926082616_remove_admin.rb @@ -0,0 +1,9 @@ +class RemoveAdmin < ActiveRecord::Migration + def up + drop_table :rails_admin_histories + end + + def down + raise "No rollback" + end +end diff --git a/db/migrate/20110927130352_create_notes.rb b/db/migrate/20110927130352_create_notes.rb new file mode 100644 index 00000000000..72a0e817617 --- /dev/null +++ b/db/migrate/20110927130352_create_notes.rb @@ -0,0 +1,12 @@ +class CreateNotes < ActiveRecord::Migration + def change + create_table :notes do |t| + t.string :note + t.integer :noteable_id + t.string :noteable_type + t.integer :author_id + + t.timestamps + end + end +end diff --git a/db/migrate/20110928140106_add_project_id_for_note.rb b/db/migrate/20110928140106_add_project_id_for_note.rb new file mode 100644 index 00000000000..3e641089799 --- /dev/null +++ b/db/migrate/20110928140106_add_project_id_for_note.rb @@ -0,0 +1,9 @@ +class AddProjectIdForNote < ActiveRecord::Migration + def up + add_column :notes, :project_id, :integer + end + + def down + remove_column :notes, :project_id, :integer + end +end diff --git a/db/migrate/20110928142747_change_noteable_id_for_note.rb b/db/migrate/20110928142747_change_noteable_id_for_note.rb new file mode 100644 index 00000000000..dc9d1f0171a --- /dev/null +++ b/db/migrate/20110928142747_change_noteable_id_for_note.rb @@ -0,0 +1,9 @@ +class ChangeNoteableIdForNote < ActiveRecord::Migration + def up + change_column :notes, :noteable_id, :string + end + + def down + change_column :notes, :noteable_id, :integer + end +end diff --git a/db/migrate/20110928161328_add_attachment_to_note.rb b/db/migrate/20110928161328_add_attachment_to_note.rb new file mode 100644 index 00000000000..37d9cf10258 --- /dev/null +++ b/db/migrate/20110928161328_add_attachment_to_note.rb @@ -0,0 +1,5 @@ +class AddAttachmentToNote < ActiveRecord::Migration + def change + add_column :notes, :attachment, :string + end +end diff --git a/db/migrate/20111005193700_add_allow_repo_creation_for_user.rb b/db/migrate/20111005193700_add_allow_repo_creation_for_user.rb new file mode 100644 index 00000000000..82bd94b8089 --- /dev/null +++ b/db/migrate/20111005193700_add_allow_repo_creation_for_user.rb @@ -0,0 +1,9 @@ +class AddAllowRepoCreationForUser < ActiveRecord::Migration + def up + add_column :users, :allowed_create_repo, :boolean, :default => true, :null => false + end + + def down + remove_column :users, :allowed_create_repo + end +end diff --git a/db/migrate/20111009101738_add_ownerto_project.rb b/db/migrate/20111009101738_add_ownerto_project.rb new file mode 100644 index 00000000000..8d265533add --- /dev/null +++ b/db/migrate/20111009101738_add_ownerto_project.rb @@ -0,0 +1,5 @@ +class AddOwnertoProject < ActiveRecord::Migration + def change + add_column :projects, :owner_id, :integer + end +end diff --git a/db/migrate/20111009110913_add_projects_limit_to_user.rb b/db/migrate/20111009110913_add_projects_limit_to_user.rb new file mode 100644 index 00000000000..8eb8382b515 --- /dev/null +++ b/db/migrate/20111009110913_add_projects_limit_to_user.rb @@ -0,0 +1,5 @@ +class AddProjectsLimitToUser < ActiveRecord::Migration + def change + add_column :users, :projects_limit, :integer, :default => 10 + end +end diff --git a/db/migrate/20111009111204_remove_allow_create_repo_from_user.rb b/db/migrate/20111009111204_remove_allow_create_repo_from_user.rb new file mode 100644 index 00000000000..61452006397 --- /dev/null +++ b/db/migrate/20111009111204_remove_allow_create_repo_from_user.rb @@ -0,0 +1,9 @@ +class RemoveAllowCreateRepoFromUser < ActiveRecord::Migration + def up + remove_column :users, :allowed_create_repo + end + + def down + add_column :users, :allowed_create_repo, :boolean, :default => true, :null => false + end +end diff --git a/db/pkey.example b/db/pkey.example new file mode 100644 index 00000000000..ae045772430 --- /dev/null +++ b/db/pkey.example @@ -0,0 +1,3 @@ +AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 +596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 +soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 00000000000..befe0b2a518 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,89 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 20111009111204) do + + create_table "issues", :force => true do |t| + t.string "title" + t.text "content" + t.integer "assignee_id" + t.integer "author_id" + t.integer "project_id" + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "closed", :default => false, :null => false + end + + create_table "keys", :force => true do |t| + t.integer "user_id", :null => false + t.datetime "created_at" + t.datetime "updated_at" + t.text "key" + t.string "title" + t.string "identifier" + end + + create_table "notes", :force => true do |t| + t.string "note" + t.string "noteable_id" + t.string "noteable_type" + t.integer "author_id" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "project_id" + t.string "attachment" + end + + create_table "projects", :force => true do |t| + t.string "name" + t.string "path" + t.text "description" + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "private_flag", :default => true, :null => false + t.string "code" + t.integer "owner_id" + end + + create_table "users", :force => true do |t| + t.string "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", :default => 0 + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at" + t.datetime "updated_at" + t.string "name" + t.boolean "admin", :default => false, :null => false + t.integer "projects_limit", :default => 10 + end + + add_index "users", ["email"], :name => "index_users_on_email", :unique => true + add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true + + create_table "users_projects", :force => true do |t| + t.integer "user_id", :null => false + t.integer "project_id", :null => false + t.boolean "read", :default => false + t.boolean "write", :default => false + t.boolean "admin", :default => false + t.datetime "created_at" + t.datetime "updated_at" + end + +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/README_FOR_APP b/doc/README_FOR_APP new file mode 100644 index 00000000000..fe41f5cc24d --- /dev/null +++ b/doc/README_FOR_APP @@ -0,0 +1,2 @@ +Use this README file to introduce your application and point to useful places in the API for learning more. +Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. diff --git a/install/prepare.rb b/install/prepare.rb new file mode 100644 index 00000000000..f85c01a01ec --- /dev/null +++ b/install/prepare.rb @@ -0,0 +1,51 @@ +module Install + class << self + def prepare(env) + puts green " == Starting for ENV=#{env} ..." + puts "rvm detected" if is_rvm? + + bundler + db(env) + + puts green " == Done! Now you can start server" + end + + def bundler + command 'gem install bundler' + command 'bundle install' + end + + def db(env) + command "bundle exec rake db:setup RAILS_ENV=#{env}" + command "bundle exec rake db:seed_fu RAILS_ENV=#{env}" + end + + def is_rvm? + `type rvm | head -1` =~ /^rvm is/ + end + + def colorize(text, color_code) + "\033[#{color_code}#{text}\033[0m" + end + + def red(text) + colorize(text, "31m") + end + + def green(text) + colorize(text, "32m") + end + + def command(string) + `#{string}` + if $?.to_i > 0 + puts red " == #{string} - FAIL" + puts red " == Error during configure" + exit + else + puts green " == #{string} - OK" + end + end + end +end + diff --git a/lib/assets/.gitkeep b/lib/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/file_size_validator.rb b/lib/file_size_validator.rb new file mode 100644 index 00000000000..151e0ce53c0 --- /dev/null +++ b/lib/file_size_validator.rb @@ -0,0 +1,65 @@ +class FileSizeValidator < ActiveModel::EachValidator + MESSAGES = { :is => :wrong_size, :minimum => :size_too_small, :maximum => :size_too_big }.freeze + CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze + + DEFAULT_TOKENIZER = lambda { |value| value.split(//) } + RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long] + + def initialize(options) + if range = (options.delete(:in) || options.delete(:within)) + raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range) + options[:minimum], options[:maximum] = range.begin, range.end + options[:maximum] -= 1 if range.exclude_end? + end + + super + end + + def check_validity! + keys = CHECKS.keys & options.keys + + if keys.empty? + raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' + end + + keys.each do |key| + value = options[key] + + unless value.is_a?(Integer) && value >= 0 + raise ArgumentError, ":#{key} must be a nonnegative Integer" + end + end + end + + def validate_each(record, attribute, value) + raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base + + value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String) + + CHECKS.each do |key, validity_check| + next unless check_value = options[key] + + value ||= [] if key == :maximum + + value_size = value.size + next if value_size.send(validity_check, check_value) + + errors_options = options.except(*RESERVED_OPTIONS) + errors_options[:file_size] = help.number_to_human_size check_value + + default_message = options[MESSAGES[key]] + errors_options[:message] ||= default_message if default_message + + record.errors.add(attribute, MESSAGES[key], errors_options) + end + end + + def help + Helper.instance + end + + class Helper + include Singleton + include ActionView::Helpers::NumberHelper + end +end diff --git a/lib/gitosis.rb b/lib/gitosis.rb new file mode 100644 index 00000000000..a9eef96cd3f --- /dev/null +++ b/lib/gitosis.rb @@ -0,0 +1,73 @@ +require 'inifile' +require 'timeout' +require 'fileutils' + +class Gitosis + class AccessDenied < StandardError; end + + def pull + # create tmp dir + @local_dir = File.join(Dir.tmpdir,"gitlabhq-gitosis-#{Time.now.to_i}") + + Dir.mkdir @local_dir + + `git clone #{GITOSIS['admin_uri']} #{@local_dir}/gitosis` + end + + def push + Dir.chdir(File.join(@local_dir, "gitosis")) + `git add -A` + `git commit -am "Gitlab"` + `git push` + Dir.chdir(Rails.root) + + FileUtils.rm_rf(@local_dir) + end + + def configure + status = Timeout::timeout(20) do + File.open(File.join(Dir.tmpdir,"gitlabhq-gitosis.lock"), "w+") do |f| + f.flock(File::LOCK_EX) + + pull + yield(self) + push + + f.flock(File::LOCK_UN) + end + end + rescue Exception => ex + raise Gitosis::AccessDenied.new("gitosis timeout") + end + + def destroy_project(project) + FileUtils.rm_rf(project.path_to_repo) + + conf = IniFile.new(File.join(@local_dir,'gitosis','gitosis.conf')) + + conf.delete_section("group #{project.path}") + + conf.write + end + + #update or create + def update_keys(user, key) + File.open(File.join(@local_dir, 'gitosis/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) } + end + + def delete_key(user) + File.unlink(File.join(@local_dir, 'gitosis/keydir',"#{user}.pub")) + `cd #{File.join(@local_dir,'gitosis')} ; git rm keydir/#{user}.pub` + end + + #update or create + def update_project(repo_name, name_writers) + # write config file + conf = IniFile.new(File.join(@local_dir,'gitosis','gitosis.conf')) + + conf["group #{repo_name}"]['writable'] = repo_name + conf["group #{repo_name}"]['members'] = name_writers.join(' ') + + conf.write + end +end diff --git a/lib/tasks/.gitkeep b/lib/tasks/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/utils.rb b/lib/utils.rb new file mode 100644 index 00000000000..6e7460ed99a --- /dev/null +++ b/lib/utils.rb @@ -0,0 +1,8 @@ +module Utils + def self.binary?(string) + string.each_byte do |x| + x.nonzero? or return true + end + false + end +end diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/public/404.html b/public/404.html new file mode 100644 index 00000000000..16d8fcbca34 --- /dev/null +++ b/public/404.html @@ -0,0 +1,25 @@ + + + + The page you were looking for doesn't exist (404) + + + + + +
      +

      404

      +

      The page you were looking for doesn't exist.

      +

      You may have mistyped the address or the page may have moved.

      +
      + + diff --git a/public/422.html b/public/422.html new file mode 100644 index 00000000000..24532664370 --- /dev/null +++ b/public/422.html @@ -0,0 +1,25 @@ + + + + The change you wanted was rejected (422) + + + + + +
      +

      422

      +

      The change you wanted was rejected.

      +

      Maybe you tried to change something you didn't have access to.

      +
      + + diff --git a/public/500.html b/public/500.html new file mode 100644 index 00000000000..b1d544647a1 --- /dev/null +++ b/public/500.html @@ -0,0 +1,25 @@ + + + + We're sorry, but something went wrong (500) + + + + + +
      +

      500

      +

      We're sorry, but something went wrong.

      +

      We've been notified about this issue and we'll take a look at it shortly.

      +
      + + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000000..e69de29bb2d diff --git a/public/gitosis_error.html b/public/gitosis_error.html new file mode 100644 index 00000000000..be751d6e5e6 --- /dev/null +++ b/public/gitosis_error.html @@ -0,0 +1,26 @@ + + + + We're sorry, but we cant get access to your gitosis + + + + + +
      +

      Gitosis Error

      +

      We're sorry, but we cant get access to your gitosis.

      +

      1. Check 'config/gitosis.yml' for correct settings.

      +

      2. Be sure web server user has access to gitosis.

      +
      + + diff --git a/public/index.html.example b/public/index.html.example new file mode 100644 index 00000000000..9d9811a5bf4 --- /dev/null +++ b/public/index.html.example @@ -0,0 +1,241 @@ + + + + Ruby on Rails: Welcome aboard + + + + +
      + + +
      + + + + +
      +

      Getting started

      +

      Here’s how to get rolling:

      + +
        +
      1. +

        Use rails generate to create your models and controllers

        +

        To see all available options, run it without parameters.

        +
      2. + +
      3. +

        Set up a default route and remove public/index.html

        +

        Routes are set up in config/routes.rb.

        +
      4. + +
      5. +

        Create your database

        +

        Run rake db:create to create your database. If you're not using SQLite (the default), edit config/database.yml with your username and password.

        +
      6. +
      +
      +
      + + +
      + + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000000..085187fa58b --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-Agent: * +# Disallow: / diff --git a/script/rails b/script/rails new file mode 100755 index 00000000000..f8da2cffd4d --- /dev/null +++ b/script/rails @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) +require 'rails/commands' diff --git a/spec/factories.rb b/spec/factories.rb new file mode 100644 index 00000000000..ea055d1bcd0 --- /dev/null +++ b/spec/factories.rb @@ -0,0 +1,45 @@ +require File.join(Rails.root, 'spec', 'factory') + +Factory.add(:project, Project) do |obj| + obj.name = Faker::Internet.user_name + obj.path = 'legit' + obj.owner = Factory(:user) + obj.code = 'LGT' +end + +Factory.add(:public_project, Project) do |obj| + obj.name = Faker::Internet.user_name + obj.path = 'legit' + obj.private_flag = false + obj.owner = Factory(:user) + obj.code = 'LGT' +end + +Factory.add(:user, User) do |obj| + obj.email = Faker::Internet.email + obj.password = "123456" + obj.name = Faker::Name.name + obj.password_confirmation = "123456" +end + +Factory.add(:admin, User) do |obj| + obj.email = Faker::Internet.email + obj.password = "123456" + obj.name = Faker::Name.name + obj.password_confirmation = "123456" + obj.admin = true +end + +Factory.add(:issue, Issue) do |obj| + obj.title = Faker::Lorem.sentence + obj.content = Faker::Lorem.sentences +end + +Factory.add(:note, Note) do |obj| + obj.note = Faker::Lorem.sentence +end + +Factory.add(:key, Key) do |obj| + obj.title = "Example key" + obj.key = File.read(File.join(Rails.root, "db", "pkey.example")) +end diff --git a/spec/factory.rb b/spec/factory.rb new file mode 100644 index 00000000000..29e552b1474 --- /dev/null +++ b/spec/factory.rb @@ -0,0 +1,29 @@ +class Factory + @factories = {} + + class << self + def add(name, klass, &block) + @factories[name] = [klass, block] + end + + def create(name, opts = {}) + new(name, opts).tap(&:save!) + end + + def new(name, opts) + factory = @factories[name] + factory[0].new.tap do |obj| + factory[1].call(obj) + end.tap do |obj| + opts.each do |k, opt| + obj.send("#{k}=", opt) + end + end + end + end +end + +def Factory(name, opts={}) + Factory.create name, opts +end + diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb new file mode 100644 index 00000000000..23609023c50 --- /dev/null +++ b/spec/models/issue_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Issue do + describe "Associations" do + it { should belong_to(:project) } + it { should belong_to(:author) } + it { should belong_to(:assignee) } + end + + describe "Validation" do + it { should validate_presence_of(:title) } + it { should validate_presence_of(:author_id) } + it { should validate_presence_of(:project_id) } + it { should validate_presence_of(:assignee_id) } + end + + describe "Scope" do + it { Issue.should respond_to :closed } + it { Issue.should respond_to :opened } + end + + it { Factory.create(:issue, + :author => Factory(:user), + :assignee => Factory(:user), + :project => Factory.create(:project)).should be_valid } + +end +# == Schema Information +# +# Table name: issues +# +# id :integer not null, primary key +# title :string(255) +# content :text +# assignee_id :integer +# author_id :integer +# project_id :integer +# created_at :datetime +# updated_at :datetime +# closed :boolean default(FALSE), not null +# + diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb new file mode 100644 index 00000000000..8515f19b2f2 --- /dev/null +++ b/spec/models/key_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Key do + describe "Associations" do + it { should belong_to(:user) } + end + + describe "Validation" do + it { should validate_presence_of(:title) } + it { should validate_presence_of(:key) } + end + + describe "Methods" do + it { should respond_to :projects } + end + + it { Factory.create(:key, + :user => Factory(:user)).should be_valid } +end +# == Schema Information +# +# Table name: keys +# +# id :integer not null, primary key +# user_id :integer not null +# created_at :datetime +# updated_at :datetime +# key :text +# title :string(255) +# identifier :string(255) +# + diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb new file mode 100644 index 00000000000..20bd41adc29 --- /dev/null +++ b/spec/models/note_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe Note do + describe "Associations" do + it { should belong_to(:project) } + end + + describe "Validation" do + it { should validate_presence_of(:note) } + it { should validate_presence_of(:project) } + end + + it { Factory.create(:note, + :project => Factory.create(:project)).should be_valid } + + describe :authorization do + before do + @p1 = Factory :project + @p2 = Factory :project, :code => "alien", :path => "legit_1" + @u1 = Factory :user + @u2 = Factory :user + @u3 = Factory :user + @abilities = Six.new + @abilities << Ability + end + + describe :read do + before do + @p1.users_projects.create(:user => @u1, :read => false) + @p1.users_projects.create(:user => @u2, :read => true) + @p2.users_projects.create(:user => @u3, :read => true) + end + + it { @abilities.allowed?(@u1, :read_note, @p1).should be_false } + it { @abilities.allowed?(@u2, :read_note, @p1).should be_true } + it { @abilities.allowed?(@u3, :read_note, @p1).should be_false } + end + + describe :write do + before do + @p1.users_projects.create(:user => @u1, :write => false) + @p1.users_projects.create(:user => @u2, :write => true) + @p2.users_projects.create(:user => @u3, :write => true) + end + + it { @abilities.allowed?(@u1, :write_note, @p1).should be_false } + it { @abilities.allowed?(@u2, :write_note, @p1).should be_true } + it { @abilities.allowed?(@u3, :write_note, @p1).should be_false } + end + + describe :admin do + before do + @p1.users_projects.create(:user => @u1, :admin => false) + @p1.users_projects.create(:user => @u2, :admin => true) + @p2.users_projects.create(:user => @u3, :admin => true) + end + + it { @abilities.allowed?(@u1, :admin_note, @p1).should be_false } + it { @abilities.allowed?(@u2, :admin_note, @p1).should be_true } + it { @abilities.allowed?(@u3, :admin_note, @p1).should be_false } + end + end +end +# == Schema Information +# +# Table name: notes +# +# id :integer not null, primary key +# note :string(255) +# noteable_id :string(255) +# noteable_type :string(255) +# author_id :integer +# created_at :datetime +# updated_at :datetime +# project_id :integer +# attachment :string(255) +# + diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb new file mode 100644 index 00000000000..8eb8ee80f6c --- /dev/null +++ b/spec/models/project_security_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe Project do + describe :authorization do + before do + @p1 = Factory :project + @u1 = Factory :user + @u2 = Factory :user + @abilities = Six.new + @abilities << Ability + end + + describe :read do + before do + @p1.users_projects.create(:project => @p1, :user => @u1, :read => false) + @p1.users_projects.create(:project => @p1, :user => @u2, :read => true) + end + + it { @abilities.allowed?(@u1, :read_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :read_project, @p1).should be_true } + end + + describe :write do + before do + @p1.users_projects.create(:project => @p1, :user => @u1, :write => false) + @p1.users_projects.create(:project => @p1, :user => @u2, :write => true) + end + + it { @abilities.allowed?(@u1, :write_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :write_project, @p1).should be_true } + end + + describe :admin do + before do + @p1.users_projects.create(:project => @p1, :user => @u1, :admin => false) + @p1.users_projects.create(:project => @p1, :user => @u2, :admin => true) + end + + it { @abilities.allowed?(@u1, :admin_project, @p1).should be_false } + it { @abilities.allowed?(@u2, :admin_project, @p1).should be_true } + end + end +end +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# description :text +# created_at :datetime +# updated_at :datetime +# private_flag :boolean default(TRUE), not null +# code :string(255) +# + diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb new file mode 100644 index 00000000000..6ed8653d761 --- /dev/null +++ b/spec/models/project_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe Project do + describe "Associations" do + it { should have_many(:users) } + it { should have_many(:users_projects) } + end + + describe "Validation" do + it { should validate_presence_of(:name) } + it { should validate_presence_of(:path) } + end + + describe "Respond to" do + it { should respond_to(:readers) } + it { should respond_to(:writers) } + it { should respond_to(:gitosis_writers) } + it { should respond_to(:admins) } + it { should respond_to(:add_access) } + it { should respond_to(:reset_access) } + it { should respond_to(:update_gitosis_project) } + it { should respond_to(:destroy_gitosis_project) } + it { should respond_to(:public?) } + it { should respond_to(:private?) } + it { should respond_to(:url_to_repo) } + it { should respond_to(:path_to_repo) } + it { should respond_to(:valid_repo?) } + it { should respond_to(:repo_exists?) } + it { should respond_to(:repo) } + it { should respond_to(:tags) } + it { should respond_to(:commit) } + end + + it "should return valid url to repo" do + project = Project.new(:path => "somewhere") + project.url_to_repo.should == "git@localhost:somewhere.git" + end + + it "should return path to repo" do + project = Project.new(:path => "somewhere") + project.path_to_repo.should == File.join(Rails.root, "tmp", "tests", "somewhere") + end + + describe :valid_repo? do + it "should be valid repo" do + project = Factory :project + project.valid_repo?.should be_true + end + + it "should be invalid repo" do + project = Project.new(:name => "ok_name", :path => "/INVALID_PATH/", :code => "NEOK") + project.valid_repo?.should be_false + end + end + + describe "Git methods" do + let(:project) { Factory :project } + + describe :repo do + it "should return valid repo" do + project.repo.should be_kind_of(Grit::Repo) + end + + it "should return nil" do + lambda { Project.new(:path => "invalid").repo }.should raise_error(Grit::NoSuchPathError) + end + + it "should return nil" do + lambda { Project.new.repo }.should raise_error(TypeError) + end + end + + describe :commit do + it "should return first head commit if without params" do + project.commit.id.should == project.repo.commits.first.id + end + + it "should return valid commit" do + project.commit(ValidCommit::ID).should be_valid_commit + end + + it "should return nil" do + project.commit("+123_4532530XYZ").should be_nil + end + end + + describe :tree do + before do + @commit = project.commit(ValidCommit::ID) + end + + it "should raise error w/o arguments" do + lambda { project.tree }.should raise_error + end + + it "should return root tree for commit" do + tree = project.tree(@commit) + tree.contents.size.should == ValidCommit::FILES_COUNT + tree.contents.map(&:name).should == ValidCommit::FILES + end + + it "should return root tree for commit with correct path" do + tree = project.tree(@commit, ValidCommit::C_FILE_PATH) + tree.contents.map(&:name).should == ValidCommit::C_FILES + end + + it "should return root tree for commit with incorrect path" do + project.tree(@commit, "invalid_path").should be_nil + end + end + end +end +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# description :text +# created_at :datetime +# updated_at :datetime +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer +# + diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 00000000000..77abe2ca935 --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe User do + describe "Associations" do + it { should have_many(:projects) } + it { should have_many(:users_projects) } + it { should have_many(:issues) } + it { should have_many(:assigned_issues) } + end + + describe "Respond to" do + it { should respond_to(:is_admin?) } + it { should respond_to(:identifier) } + it { should respond_to(:name) } + end + + it "should return valid identifier" do + user = User.new(:email => "test@mail.com") + user.identifier.should == "test_mail.com" + end +end +# == Schema Information +# +# Table name: users +# +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(128) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer +# + diff --git a/spec/models/users_project_spec.rb b/spec/models/users_project_spec.rb new file mode 100644 index 00000000000..d6a4d02b6e2 --- /dev/null +++ b/spec/models/users_project_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe UsersProject do + describe "Associations" do + it { should belong_to(:project) } + it { should belong_to(:user) } + end + + describe "Validation" do + it { should validate_presence_of(:user_id) } + it { should validate_presence_of(:project_id) } + end + + describe "Delegate methods" do + it { should respond_to(:user_name) } + it { should respond_to(:user_email) } + end +end +# == Schema Information +# +# Table name: users_projects +# +# id :integer not null, primary key +# user_id :integer not null +# project_id :integer not null +# read :boolean default(FALSE) +# write :boolean default(FALSE) +# admin :boolean default(FALSE) +# created_at :datetime +# updated_at :datetime +# + diff --git a/spec/monkeypatch.rb b/spec/monkeypatch.rb new file mode 100644 index 00000000000..ea42337f931 --- /dev/null +++ b/spec/monkeypatch.rb @@ -0,0 +1,31 @@ +# Stubbing Project <-> gitosis path +# create project using Factory only +class Project + def update_gitosis_project + true + end + + def update_gitosis + true + end + + def path_to_repo + File.join(Rails.root, "tmp", "tests", path) + end +end + +class Key + def update_gitosis + true + end + + def gitosis_delete_key + true + end +end + +class UsersProject + def update_gitosis_project + true + end +end diff --git a/spec/requests/admin/admin_projects_spec.rb b/spec/requests/admin/admin_projects_spec.rb new file mode 100644 index 00000000000..8aa311e8442 --- /dev/null +++ b/spec/requests/admin/admin_projects_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +describe "Admin::Projects" do + before do + @project = Factory :project, + :name => "LeGiT", + :code => "LGT" + login_as :admin + end + + describe "GET /admin/projects" do + before do + visit admin_projects_path + end + + it "should be ok" do + current_path.should == admin_projects_path + end + + it "should have projects list" do + page.should have_content(@project.code) + page.should have_content(@project.name) + end + end + + describe "GET /admin/projects/:id" do + before do + visit admin_projects_path + click_link "Show" + end + + it "should have project info" do + page.should have_content(@project.code) + page.should have_content(@project.name) + end + end + + describe "GET /admin/projects/:id/edit" do + before do + visit admin_projects_path + click_link "edit_project_#{@project.id}" + end + + it "should have project edit page" do + page.should have_content("Name") + page.should have_content("Code") + end + + describe "Update project" do + before do + fill_in "project_name", :with => "Big Bang" + fill_in "project_code", :with => "BB1" + click_button "Save" + @project.reload + end + + it "should show page with new data" do + page.should have_content("BB1") + page.should have_content("Big Bang") + end + + it "should change project entry" do + @project.name.should == "Big Bang" + @project.code.should == "BB1" + end + end + end + + describe "GET /admin/projects/new" do + before do + visit admin_projects_path + click_link "New Project" + end + + it "should be correct path" do + current_path.should == new_admin_project_path + end + + it "should have labels for new project" do + page.should have_content("Name") + page.should have_content("Path") + page.should have_content("Description") + end + end + + describe "POST /admin/projects" do + before do + visit new_admin_project_path + fill_in 'Name', :with => 'NewProject' + fill_in 'Code', :with => 'NPR' + fill_in 'Path', :with => '/tmp/legit_test/legit' + expect { click_button "Save" }.to change { Project.count }.by(1) + @project = Project.last + end + + it "should be correct path" do + current_path.should == admin_project_path(@project) + end + + it "should show project" do + page.should have_content(@project.name) + page.should have_content(@project.path) + page.should have_content(@project.description) + end + end +end diff --git a/spec/requests/admin/admin_users_spec.rb b/spec/requests/admin/admin_users_spec.rb new file mode 100644 index 00000000000..8d9cbcae2ac --- /dev/null +++ b/spec/requests/admin/admin_users_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe "Admin::Users" do + before { login_as :admin } + + describe "GET /admin/users" do + before do + visit admin_users_path + end + + it "should be ok" do + current_path.should == admin_users_path + end + + it "should have users list" do + page.should have_content(@user.email) + page.should have_content(@user.name) + end + end + + describe "GET /admin/users/new" do + before do + @password = "123ABC" + visit new_admin_user_path + fill_in "user_name", :with => "Big Bang" + fill_in "user_email", :with => "bigbang@mail.com" + fill_in "user_password", :with => @password + fill_in "user_password_confirmation", :with => @password + end + + it "should create new user" do + expect { click_button "Save" }.to change {User.count}.by(1) + end + + it "should create user with valid data" do + click_button "Save" + user = User.last + user.name.should == "Big Bang" + user.email.should == "bigbang@mail.com" + end + + it "should call send mail" do + Notify.should_receive(:new_user_email).and_return(stub(:deliver => true)) + click_button "Save" + end + + it "should send valid email to user with email & password" do + click_button "Save" + user = User.last + email = ActionMailer::Base.deliveries.last + email.subject.should have_content("Account was created") + email.body.should have_content(user.email) + email.body.should have_content(@password) + end + end + + describe "GET /admin/users/:id" do + before do + visit admin_users_path + click_link "Show" + end + + it "should have user info" do + page.should have_content(@user.email) + page.should have_content(@user.name) + page.should have_content(@user.is_admin?) + end + end + + describe "GET /admin/users/:id/edit" do + before do + @simple_user = Factory :user + visit admin_users_path + click_link "edit_user_#{@simple_user.id}" + end + + it "should have user edit page" do + page.should have_content("Name") + page.should have_content("Password") + end + + describe "Update user" do + before do + fill_in "user_name", :with => "Big Bang" + fill_in "user_email", :with => "bigbang@mail.com" + check "user_admin" + click_button "Save" + end + + it "should show page with new data" do + page.should have_content("bigbang@mail.com") + page.should have_content("Big Bang") + end + + it "should change user entry" do + @simple_user.reload + @simple_user.name.should == "Big Bang" + @simple_user.is_admin?.should be_true + end + end + end +end diff --git a/spec/requests/admin/security_spec.rb b/spec/requests/admin/security_spec.rb new file mode 100644 index 00000000000..743f9f08cef --- /dev/null +++ b/spec/requests/admin/security_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "Admin::Projects" do + describe "GET /admin/projects" do + it { admin_projects_path.should be_allowed_for :admin } + it { admin_projects_path.should be_denied_for :user } + it { admin_projects_path.should be_denied_for :visitor } + end + + describe "GET /admin/users" do + it { admin_users_path.should be_allowed_for :admin } + it { admin_users_path.should be_denied_for :user } + it { admin_users_path.should be_denied_for :visitor } + end + + describe "GET /admin/team_members" do + it { admin_team_members_path.should be_allowed_for :admin } + it { admin_team_members_path.should be_denied_for :user } + it { admin_team_members_path.should be_denied_for :visitor } + end + + describe "GET /admin/emails" do + it { admin_emails_path.should be_allowed_for :admin } + it { admin_emails_path.should be_denied_for :user } + it { admin_emails_path.should be_denied_for :visitor } + end +end diff --git a/spec/requests/commits_notes_spec.rb b/spec/requests/commits_notes_spec.rb new file mode 100644 index 00000000000..522fe51824b --- /dev/null +++ b/spec/requests/commits_notes_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe "Issues" do + let(:project) { Factory :project } + let!(:commit) { project.repo.commits.first } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "add new note", :js => true do + before do + visit project_commit_path(project, commit) + click_link "Comments" # notes tab + fill_in "note_note", :with => "I commented this commit" + click_button "Add note" + end + + it "should conatin new note" do + page.should have_content("I commented this commit") + end + end +end diff --git a/spec/requests/commits_spec.rb b/spec/requests/commits_spec.rb new file mode 100644 index 00000000000..cd7314f9812 --- /dev/null +++ b/spec/requests/commits_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe "Commits" do + let(:project) { Factory :project } + let!(:commit) { project.repo.commits.first } + before do + login_as :user + project.add_access(@user, :read) + end + + describe "GET /commits" do + before do + visit project_commits_path(project) + end + + it "should have valid path" do + current_path.should == project_commits_path(project) + end + + it "should have project name" do + page.should have_content(project.name) + end + + it "should list commits" do + page.should have_content(commit.author) + page.should have_content(commit.message) + end + end + + describe "GET /commits/:id" do + before do + visit project_commit_path(project, commit) + end + + it "should have valid path" do + current_path.should == project_commit_path(project, commit) + end + end +end diff --git a/spec/requests/issues_notes_spec.rb b/spec/requests/issues_notes_spec.rb new file mode 100644 index 00000000000..95eb2f35d79 --- /dev/null +++ b/spec/requests/issues_notes_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "Issues" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + + @issue = Factory :issue, + :author => @user, + :assignee => @user, + :project => project + end + + describe "add new note", :js => true do + before do + visit project_issue_path(project, @issue) + fill_in "note_note", :with => "I commented this issue" + click_button "Add note" + end + + it "should conatin new note" do + page.should have_content("I commented this issue") + end + end +end diff --git a/spec/requests/issues_spec.rb b/spec/requests/issues_spec.rb new file mode 100644 index 00000000000..79fdf5efbb5 --- /dev/null +++ b/spec/requests/issues_spec.rb @@ -0,0 +1,147 @@ +require 'spec_helper' + +describe "Issues" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "GET /issues" do + before do + @issue = Factory :issue, + :author => @user, + :assignee => @user, + :project => project + + visit project_issues_path(project) + end + + subject { page } + + it { should have_content(@issue.title) } + it { should have_content(@issue.project.name) } + it { should have_content(@issue.assignee.name) } + + describe "Destroy" do + before do + # admin access to remove issue + @user.users_projects.destroy_all + project.add_access(@user, :read, :write, :admin) + visit project_issues_path(project) + end + + it "should remove entry" do + expect { + click_link "destroy_issue_#{@issue.id}" + }.to change { Issue.count }.by(-1) + end + end + + describe "statuses", :js => true do + before do + @closed_issue = Factory :issue, + :author => @user, + :assignee => @user, + :project => project, + :closed => true + end + + it "should show only open" do + should have_content(@issue.title) + should have_no_content(@closed_issue.title) + end + + it "should show only closed" do + choose "closed_issues" + should have_no_content(@issue.title) + should have_content(@closed_issue.title) + end + + it "should show all" do + choose "all_issues" + should have_content(@issue.title) + should have_content(@closed_issue.title) + end + end + end + + describe "New issue", :js => true do + before do + visit project_issues_path(project) + click_link "New Issue" + end + + it "should open new issue popup" do + page.should have_content("Add new issue") + end + + describe "fill in" do + before do + fill_in "issue_title", :with => "bug 345" + fill_in "issue_content", :with => "app bug 345" + click_link "Select user" + click_link @user.name + end + + it { expect { click_button "Save" }.to change {Issue.count}.by(1) } + + it "should add new issue to table" do + click_button "Save" + + page.should_not have_content("Add new issue") + page.should have_content @user.name + page.should have_content "bug 345" + page.should have_content project.name + end + + it "should call send mail" do + Notify.should_receive(:new_issue_email).and_return(stub(:deliver => true)) + click_button "Save" + end + + it "should send valid email to user with email & password" do + click_button "Save" + issue = Issue.last + email = ActionMailer::Base.deliveries.last + email.subject.should have_content("New Issue was created") + email.body.should have_content(issue.title) + email.body.should have_content(issue.assignee.name) + end + end + end + + describe "Edit issue", :js => true do + before do + @issue = Factory :issue, + :author => @user, + :assignee => @user, + :project => project + visit project_issues_path(project) + click_link "Edit" + end + + it "should open new issue popup" do + page.should have_content("Issue ##{@issue.id}") + end + + describe "fill in" do + before do + fill_in "issue_title", :with => "bug 345" + fill_in "issue_content", :with => "app bug 345" + end + + it { expect { click_button "Save" }.to_not change {Issue.count} } + + it "should update issue fields" do + click_button "Save" + + page.should_not have_content("Issue ##{@issue.id}") + page.should have_content @user.name + page.should have_content "bug 345" + page.should have_content project.name + end + end + end +end diff --git a/spec/requests/keys_spec.rb b/spec/requests/keys_spec.rb new file mode 100644 index 00000000000..316115f9234 --- /dev/null +++ b/spec/requests/keys_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe "Issues" do + before do + login_as :user + end + + describe "GET /keys" do + before do + @key = Factory :key, :user => @user + visit keys_path + end + + subject { page } + + it { should have_content(@key.title) } + + describe "Destroy" do + it "should remove entry" do + expect { + click_link "destroy_key_#{@key.id}" + }.to change { @user.keys.count }.by(-1) + end + end + end + + describe "New key", :js => true do + before do + visit keys_path + click_link "Add new" + end + + it "should open new key popup" do + page.should have_content("Add new public key") + end + + describe "fill in" do + before do + fill_in "key_title", :with => "laptop" + fill_in "key_key", :with => "publickey234=" + end + + it { expect { click_button "Save" }.to change {Key.count}.by(1) } + + it "should add new key to table" do + click_button "Save" + + page.should_not have_content("Add new public key") + page.should have_content "laptop" + page.should have_content "publickey234=" + end + end + end +end diff --git a/spec/requests/profile_spec.rb b/spec/requests/profile_spec.rb new file mode 100644 index 00000000000..07fdc4abdc6 --- /dev/null +++ b/spec/requests/profile_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe "Profile" do + before do + login_as :user + end + + describe "Show profile" do + before do + visit profile_path + end + + it { page.should have_content(@user.name) } + it { page.should have_content(@user.email) } + end + + describe "Password update" do + before do + visit profile_password_path + end + + it { page.should have_content("Password") } + it { page.should have_content("Password confirmation") } + + describe "change password" do + before do + @old_pwd = @user.encrypted_password + fill_in "user_password", :with => "777777" + fill_in "user_password_confirmation", :with => "777777" + click_button "Save" + @user.reload + end + + it "should redirect to signin page" do + current_path.should == new_user_session_path + end + + it "should change password" do + @user.encrypted_password.should_not == @old_pwd + end + + describe "login with new password" do + before do + fill_in "user_email", :with => @user.email + fill_in "user_password", :with => "777777" + click_button "Sign in" + end + + it "should login user" do + current_path.should == root_path + end + end + end + end +end diff --git a/spec/requests/projects_security_spec.rb b/spec/requests/projects_security_spec.rb new file mode 100644 index 00000000000..a725a49c2d8 --- /dev/null +++ b/spec/requests/projects_security_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +describe "Projects" do + describe "GET /projects" do + it { projects_path.should be_allowed_for :admin } + it { projects_path.should be_allowed_for :user } + it { projects_path.should be_denied_for :visitor } + end + + describe "GET /projects/new" do + it { projects_path.should be_allowed_for :admin } + it { projects_path.should be_allowed_for :user } + it { projects_path.should be_denied_for :visitor } + end + + describe "Project" do + before do + @project = Factory :project + @u1 = Factory :user + @u2 = Factory :user + @u3 = Factory :user + # full access + @project.users_projects.create(:user => @u1, :read => true, :write => true, :admin => true) + # no access + @project.users_projects.create(:user => @u2, :read => false, :write => false, :admin => false) + # readonly + @project.users_projects.create(:user => @u3, :read => true, :write => false, :admin => false) + end + + describe "GET /project_code" do + it { project_path(@project).should be_allowed_for @u1 } + it { project_path(@project).should be_allowed_for @u3 } + it { project_path(@project).should be_denied_for :admin } + it { project_path(@project).should be_denied_for @u2 } + it { project_path(@project).should be_denied_for :user } + it { project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/tree" do + it { tree_project_path(@project).should be_allowed_for @u1 } + it { tree_project_path(@project).should be_allowed_for @u3 } + it { tree_project_path(@project).should be_denied_for :admin } + it { tree_project_path(@project).should be_denied_for @u2 } + it { tree_project_path(@project).should be_denied_for :user } + it { tree_project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/commits" do + it { project_commits_path(@project).should be_allowed_for @u1 } + it { project_commits_path(@project).should be_allowed_for @u3 } + it { project_commits_path(@project).should be_denied_for :admin } + it { project_commits_path(@project).should be_denied_for @u2 } + it { project_commits_path(@project).should be_denied_for :user } + it { project_commits_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/commit" do + it { project_commit_path(@project, @project.commit).should be_allowed_for @u1 } + it { project_commit_path(@project, @project.commit).should be_allowed_for @u3 } + it { project_commit_path(@project, @project.commit).should be_denied_for :admin } + it { project_commit_path(@project, @project.commit).should be_denied_for @u2 } + it { project_commit_path(@project, @project.commit).should be_denied_for :user } + it { project_commit_path(@project, @project.commit).should be_denied_for :visitor } + end + + describe "GET /project_code/team" do + it { team_project_path(@project).should be_allowed_for @u1 } + it { team_project_path(@project).should be_allowed_for @u3 } + it { team_project_path(@project).should be_denied_for :admin } + it { team_project_path(@project).should be_denied_for @u2 } + it { team_project_path(@project).should be_denied_for :user } + it { team_project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/wall" do + it { wall_project_path(@project).should be_allowed_for @u1 } + it { wall_project_path(@project).should be_allowed_for @u3 } + it { wall_project_path(@project).should be_denied_for :admin } + it { wall_project_path(@project).should be_denied_for @u2 } + it { wall_project_path(@project).should be_denied_for :user } + it { wall_project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/blob" do + it { blob_project_path(@project).should be_allowed_for @u1 } + it { blob_project_path(@project).should be_allowed_for @u3 } + it { blob_project_path(@project).should be_denied_for :admin } + it { blob_project_path(@project).should be_denied_for @u2 } + it { blob_project_path(@project).should be_denied_for :user } + it { blob_project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/edit" do + it { edit_project_path(@project).should be_allowed_for @u1 } + it { edit_project_path(@project).should be_denied_for @u3 } + it { edit_project_path(@project).should be_denied_for :admin } + it { edit_project_path(@project).should be_denied_for @u2 } + it { edit_project_path(@project).should be_denied_for :user } + it { edit_project_path(@project).should be_denied_for :visitor } + end + + describe "GET /project_code/issues" do + it { project_issues_path(@project).should be_allowed_for @u1 } + it { project_issues_path(@project).should be_allowed_for @u3 } + it { project_issues_path(@project).should be_denied_for :admin } + it { project_issues_path(@project).should be_denied_for @u2 } + it { project_issues_path(@project).should be_denied_for :user } + it { project_issues_path(@project).should be_denied_for :visitor } + end + end +end diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb new file mode 100644 index 00000000000..329f0a50435 --- /dev/null +++ b/spec/requests/projects_spec.rb @@ -0,0 +1,152 @@ +require 'spec_helper' + +describe "Projects" do + before { login_as :user } + + describe "GET /projects" do + before do + visit projects_path + end + + it "should be on projects page" do + current_path.should == projects_path + end + + it "should have link to new project" do + page.should have_content("New Project") + end + end + + describe "GET /projects/new" do + before do + visit projects_path + click_link "New Project" + end + + it "should be correct path" do + current_path.should == new_project_path + end + + it "should have labels for new project" do + page.should have_content("Name") + page.should have_content("Path") + page.should have_content("Description") + end + end + + describe "POST /projects" do + before do + visit new_project_path + fill_in 'Name', :with => 'NewProject' + fill_in 'Code', :with => 'NPR' + fill_in 'Path', :with => '/tmp/legit_test/legit' + expect { click_button "Create Project" }.to change { Project.count }.by(1) + @project = Project.last + end + + it "should be correct path" do + current_path.should == project_path(@project) + end + + it "should show project" do + page.should have_content(@project.name) + page.should have_content(@project.path) + page.should have_content(@project.description) + end + + it "should init repo instructions" do + page.should have_content("git remote") + page.should have_content(@project.url_to_repo) + end + end + + describe "GET /projects/show" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit project_path(@project) + end + + it "should be correct path" do + current_path.should == project_path(@project) + end + + it_behaves_like :tree_view + end + + describe "GET /projects/team" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit team_project_path(@project, + :path => ValidCommit::BLOB_FILE_PATH, + :commit_id => ValidCommit::ID) + end + + it "should be correct path" do + current_path.should == team_project_path(@project) + end + + it "should have as as team member" do + page.should have_content(@user.name) + end + end + + describe "GET /projects/:id/edit" do + before do + @project = Factory :project + @project.add_access(@user, :admin, :read) + + visit edit_project_path(@project) + end + + it "should be correct path" do + current_path.should == edit_project_path(@project) + end + + it "should have labels for new project" do + page.should have_content("Name") + page.should have_content("Path") + page.should have_content("Description") + end + end + + describe "PUT /projects/:id" do + before do + @project = Factory :project + @project.add_access(@user, :admin, :read) + + visit edit_project_path(@project) + + fill_in 'Name', :with => 'Awesome' + fill_in 'Path', :with => 'legit' + fill_in 'Description', :with => 'Awesome project' + click_button "Update Project" + @project = @project.reload + end + + it "should be correct path" do + current_path.should == project_path(@project) + end + + it "should show project" do + page.should have_content("Awesome") + end + + it_behaves_like :tree_view + end + + #describe "DELETE /projects/:id", :js => true do + #before do + #@project = Factory :project + #@project.add_access(@user, :read, :admin) + #visit projects_path + #end + + #it "should be correct path" do + #expect { click_link "Destroy" }.to change {Project.count}.by(1) + #end + #end +end diff --git a/spec/requests/projects_tree_spec.rb b/spec/requests/projects_tree_spec.rb new file mode 100644 index 00000000000..4e3176bbdc3 --- /dev/null +++ b/spec/requests/projects_tree_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe "Projects" do + before { login_as :user } + + describe "GET /projects/tree" do + describe "head" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit tree_project_path(@project) + end + + it "should be correct path" do + current_path.should == tree_project_path(@project) + end + + it_behaves_like :tree_view + end + + describe ValidCommit::ID do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit tree_project_path(@project, :commit_id => ValidCommit::ID) + end + + it "should be correct path" do + current_path.should == tree_project_path(@project) + end + + it_behaves_like :tree_view + it_behaves_like :project_side_pane + end + + describe "branch passed" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit tree_project_path(@project, :branch => "master") + end + + it "should be correct path" do + current_path.should == tree_project_path(@project) + end + + it_behaves_like :tree_view + it_behaves_like :project_side_pane + end + + # TREE FILE PREVIEW + describe "file preview" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit tree_project_path(@project, :path => ".rvmrc") + end + + it "should be correct path" do + current_path.should == tree_project_path(@project) + end + + it "should contain file view" do + page.should have_content("rvm use 1.9.2@legit") + end + end + end + + # RAW FILE + describe "GET /projects/blob" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + visit blob_project_path(@project, + :path => ValidCommit::BLOB_FILE_PATH, + :commit_id => ValidCommit::ID) + end + + it "should be correct path" do + current_path.should == blob_project_path(@project) + end + + it "raw file response" do + page.source.should == ValidCommit::BLOB_FILE + end + end +end diff --git a/spec/requests/projects_wall_spec.rb b/spec/requests/projects_wall_spec.rb new file mode 100644 index 00000000000..a7387f9326d --- /dev/null +++ b/spec/requests/projects_wall_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe "Projects", "Wall" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "View notes on wall" do + before do + Factory :note, :project => project, :note => "Project specs", :author => @user + visit wall_project_path(project) + end + + it { page.should have_content("Project specs") } + it { page.should have_content(@user.name) } + it { page.should have_content("less than a minute ago") } + end + + describe "add new note", :js => true do + before do + visit wall_project_path(project) + fill_in "note_note", :with => "my post on wall" + click_button "Add note" + end + + it "should conatin new note" do + page.should have_content("my post on wall") + end + end +end diff --git a/spec/requests/team_members_spec.rb b/spec/requests/team_members_spec.rb new file mode 100644 index 00000000000..db7513ae956 --- /dev/null +++ b/spec/requests/team_members_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe "TeamMembers" do + before do + login_as :user + @project = Factory :project + @project.add_access(@user, :read, :admin) + end + + describe "New Team member", :js => true do + before do + @user_1 = Factory :user + visit team_project_path(@project) + click_link "Add new" + end + + it "should open new team member popup" do + page.should have_content("Add new member to project") + end + + describe "fill in" do + before do + check "team_member_read" + click_link "Select user" + click_link @user_1.name + #select @user_1.name, :from => "team_member_user_id" + end + + it { expect { click_button "Save" }.to change {UsersProject.count}.by(1) } + + it "should add new member to table" do + click_button "Save" + + page.should_not have_content("Add new member") + page.should have_content @user_1.name + end + end + end + + describe "Cancel membership" do + it "should cancel membership" do + visit team_project_path(@project) + expect { click_link "Cancel" }.to change { UsersProject.count }.by(-1) + end + end +end diff --git a/spec/requests/top_panel_spec.rb b/spec/requests/top_panel_spec.rb new file mode 100644 index 00000000000..bd4d2047796 --- /dev/null +++ b/spec/requests/top_panel_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe "Top Panel", :js => true do + before { login_as :user } + + describe "Search autocomplete" do + before do + visit projects_path + fill_in "search", :with => "Ke" + sleep(2) + find(:xpath, "//ul[contains(@class,'ui-autocomplete')]/li/a[.=\"Keys\"]").click + end + + it "should be on projects page" do + current_path.should == keys_path + end + end + + describe "with project" do + before do + @project = Factory :project + @project.add_access(@user, :read) + visit project_path(@project) + + fill_in "search", :with => "Commi" + sleep(2) + find(:xpath, "//ul[contains(@class,'ui-autocomplete')]/li/a[.=\"#{@project.code} / Commits\"]").click + end + + it "should be on projects page" do + current_path.should == project_commits_path(@project) + end + end +end diff --git a/spec/requests/user_security_spec.rb b/spec/requests/user_security_spec.rb new file mode 100644 index 00000000000..3c923870e86 --- /dev/null +++ b/spec/requests/user_security_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe "Users Security" do + describe "Project" do + before do + @u1 = Factory :user + end + + describe "GET /login" do + it { new_user_session_path.should be_denied_for @u1 } + it { new_user_session_path.should be_denied_for :admin } + it { new_user_session_path.should be_denied_for :user } + it { new_user_session_path.should be_allowed_for :visitor } + end + + describe "GET /keys" do + it { keys_path.should be_allowed_for @u1 } + it { keys_path.should be_allowed_for :admin } + it { keys_path.should be_allowed_for :user } + it { keys_path.should be_denied_for :visitor } + end + + describe "GET /profile" do + it { profile_path.should be_allowed_for @u1 } + it { profile_path.should be_allowed_for :admin } + it { profile_path.should be_allowed_for :user } + it { profile_path.should be_denied_for :visitor } + end + + describe "GET /profile/password" do + it { profile_password_path.should be_allowed_for @u1 } + it { profile_password_path.should be_allowed_for :admin } + it { profile_password_path.should be_allowed_for :user } + it { profile_password_path.should be_denied_for :visitor } + end + end +end diff --git a/spec/seed_project.tar.gz b/spec/seed_project.tar.gz new file mode 100644 index 00000000000..6474c324f07 Binary files /dev/null and b/spec/seed_project.tar.gz differ diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000000..0c2545e3380 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,56 @@ +require 'simplecov' +SimpleCov.start 'rails' + +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'capybara/rails' +require 'capybara/rspec' +require 'capybara/dsl' +require 'factories' +require 'monkeypatch' + + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} + +RSpec.configure do |config| + # == Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + config.mock_with :rspec + + config.include LoginMacros + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = false + + config.before :each, :type => :integration do + DeviseSessionMock.disable + end + + config.before do + if example.metadata[:js] + DatabaseCleaner.strategy = :truncation + else + DatabaseCleaner.strategy = :transaction + end + + DatabaseCleaner.start + end + + config.after do + DatabaseCleaner.clean + end +end diff --git a/spec/support/js_patch.rb b/spec/support/js_patch.rb new file mode 100644 index 00000000000..0d4ab264e85 --- /dev/null +++ b/spec/support/js_patch.rb @@ -0,0 +1,6 @@ +module JsPatch + def confirm_js_popup + page.evaluate_script("window.alert = function(msg) { return true; }") + page.evaluate_script("window.confirm = function(msg) { return true; }") + end +end diff --git a/spec/support/login.rb b/spec/support/login.rb new file mode 100644 index 00000000000..09f64f9e3c3 --- /dev/null +++ b/spec/support/login.rb @@ -0,0 +1,29 @@ +module LoginMacros + def login_as role + @user = User.create(:email => "user#{User.count}@mail.com", + :name => "John Smith", + :password => "123456", + :password_confirmation => "123456") + + if role == :admin + @user.admin = true + @user.save! + end + + visit new_user_session_path + fill_in "Email", :with => @user.email + fill_in "Password", :with => "123456" + click_button "Sign in" + end + + def login_with(user) + visit new_user_session_path + fill_in "Email", :with => user.email + fill_in "Password", :with => "123456" + click_button "Sign in" + end + + def logout + click_link "Logout" rescue nil + end +end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb new file mode 100644 index 00000000000..953b535698d --- /dev/null +++ b/spec/support/matchers.rb @@ -0,0 +1,46 @@ +RSpec::Matchers.define :be_valid_commit do + match do |actual| + actual != nil + actual.id == ValidCommit::ID + actual.message == ValidCommit::MESSAGE + actual.author.name == ValidCommit::AUTHOR_FULL_NAME + end +end + +RSpec::Matchers.define :be_allowed_for do |user| + match do |url| + include UrlAccess + url_allowed?(user, url) + end +end + +RSpec::Matchers.define :be_denied_for do |user| + match do |url| + include UrlAccess + url_denied?(user, url) + end +end + +module UrlAccess + def url_allowed?(user, url) + emulate_user(user) + visit url + result = (current_path == url) + end + + def url_denied?(user, url) + emulate_user(user) + visit url + result = (current_path != url) + end + + def emulate_user(user) + user = case user + when :user then Factory(:user) + when :visitor then nil + when :admin then Factory(:admin) + else user + end + login_with(user) if user + end +end diff --git a/spec/support/security.rb b/spec/support/security.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb new file mode 100644 index 00000000000..a2e94ac109b --- /dev/null +++ b/spec/support/shared_examples.rb @@ -0,0 +1,18 @@ +shared_examples_for :project_side_pane do + subject { page } + it { should have_content((@project || project).name) } + it { should have_content("Commits") } + it { should have_content("Team") } + it { should have_content("Tree") } +end + + +shared_examples_for :tree_view do + subject { page } + + it "should have Tree View of project" do + should have_content("app") + should have_content("history") + should have_content("Gemfile") + end +end diff --git a/spec/support/valid_commit.rb b/spec/support/valid_commit.rb new file mode 100644 index 00000000000..a4d3496e241 --- /dev/null +++ b/spec/support/valid_commit.rb @@ -0,0 +1,25 @@ +module ValidCommit + ID = "eaffbe556ec3a8dc84ef15892a9f12d84dde7e1d" + MESSAGE = "style" + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" + + FILES = [".gitignore", ".rspec", ".rvmrc", "Gemfile", "Gemfile.lock", "LICENSE", "README.rdoc", "Rakefile", "app", "config.ru", "config", "db", "doc", "lib", "log", "public", "script", "spec", "vendor"] + FILES_COUNT = 19 + + C_FILE_PATH = "app/models" + C_FILES = [".gitkeep", "project.rb", "user.rb"] + + BLOB_FILE = <<-blob +
      +

      Tree / <%= link_to "Commits", project_commits_path(@project) %>

      + <%= render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @commit.tree} %> +
      + +
      + <%= render "side_panel" %> +
      +blob + + BLOB_FILE_PATH = "app/views/projects/show.html.erb" +end + diff --git a/vendor/assets/stylesheets/.gitkeep b/vendor/assets/stylesheets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/assets/stylesheets/blueprint/ie.css b/vendor/assets/stylesheets/blueprint/ie.css new file mode 100644 index 00000000000..f01539959a4 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/ie.css @@ -0,0 +1,36 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* ie.css */ +body {text-align:center;} +.container {text-align:left;} +* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 {display:inline;overflow-x:hidden;} +* html legend {margin:0px -8px 16px 0;padding:0;} +sup {vertical-align:text-top;} +sub {vertical-align:text-bottom;} +html>body p code {*white-space:normal;} +hr {margin:-8px auto 11px;} +img {-ms-interpolation-mode:bicubic;} +.clearfix, .container {display:inline-block;} +* html .clearfix, * html .container {height:1%;} +fieldset {padding-top:0;} +legend {margin-top:-0.2em;margin-bottom:1em;margin-left:-0.5em;} +textarea {overflow:auto;} +label {vertical-align:middle;position:relative;top:-0.25em;} +input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;} +input.text:focus, input.title:focus {border-color:#666;} +input.text, input.title, textarea, select {margin:0.5em 0;} +input.checkbox, input.radio {position:relative;top:.25em;} +form.inline div, form.inline p {vertical-align:middle;} +form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;} +button, input.button {position:relative;top:0.25em;} \ No newline at end of file diff --git a/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/cross.png b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/cross.png new file mode 100755 index 00000000000..1514d51a3cf Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/cross.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/key.png b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/key.png new file mode 100755 index 00000000000..a9d5e4f8cc7 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/key.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/tick.png b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/tick.png new file mode 100755 index 00000000000..a9925a06ab0 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/buttons/icons/tick.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/buttons/readme.txt b/vendor/assets/stylesheets/blueprint/plugins/buttons/readme.txt new file mode 100644 index 00000000000..aa9fe26ba27 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/buttons/readme.txt @@ -0,0 +1,32 @@ +Buttons + +* Gives you great looking CSS buttons, for both and + + + Change Password + + + + Cancel + diff --git a/vendor/assets/stylesheets/blueprint/plugins/buttons/screen.css b/vendor/assets/stylesheets/blueprint/plugins/buttons/screen.css new file mode 100644 index 00000000000..bb66b2154d9 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/buttons/screen.css @@ -0,0 +1,97 @@ +/* -------------------------------------------------------------- + + buttons.css + * Gives you some great CSS-only buttons. + + Created by Kevin Hale [particletree.com] + * particletree.com/features/rediscovering-the-button-element + + See Readme.txt in this folder for instructions. + +-------------------------------------------------------------- */ + +a.button, button { + display:block; + float:left; + margin: 0.7em 0.5em 0.7em 0; + padding:5px 10px 5px 7px; /* Links */ + + border:1px solid #dedede; + border-top:1px solid #eee; + border-left:1px solid #eee; + + background-color:#f5f5f5; + font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif; + font-size:100%; + line-height:130%; + text-decoration:none; + font-weight:bold; + color:#565656; + cursor:pointer; +} +button { + width:auto; + overflow:visible; + padding:4px 10px 3px 7px; /* IE6 */ +} +button[type] { + padding:4px 10px 4px 7px; /* Firefox */ + line-height:17px; /* Safari */ +} +*:first-child+html button[type] { + padding:4px 10px 3px 7px; /* IE7 */ +} +button img, a.button img{ + margin:0 3px -3px 0 !important; + padding:0; + border:none; + width:16px; + height:16px; + float:none; +} + + +/* Button colors +-------------------------------------------------------------- */ + +/* Standard */ +button:hover, a.button:hover{ + background-color:#dff4ff; + border:1px solid #c2e1ef; + color:#336699; +} +a.button:active{ + background-color:#6299c5; + border:1px solid #6299c5; + color:#fff; +} + +/* Positive */ +body .positive { + color:#529214; +} +a.positive:hover, button.positive:hover { + background-color:#E6EFC2; + border:1px solid #C6D880; + color:#529214; +} +a.positive:active { + background-color:#529214; + border:1px solid #529214; + color:#fff; +} + +/* Negative */ +body .negative { + color:#d12f19; +} +a.negative:hover, button.negative:hover { + background-color:#fbe3e4; + border:1px solid #fbc2c4; + color:#d12f19; +} +a.negative:active { + background-color:#d12f19; + border:1px solid #d12f19; + color:#fff; +} diff --git a/vendor/assets/stylesheets/blueprint/plugins/fancy-type/readme.txt b/vendor/assets/stylesheets/blueprint/plugins/fancy-type/readme.txt new file mode 100644 index 00000000000..85f24915223 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/fancy-type/readme.txt @@ -0,0 +1,14 @@ +Fancy Type + +* Gives you classes to use if you'd like some + extra fancy typography. + +Credits and instructions are specified above each class +in the fancy-type.css file in this directory. + + +Usage +---------------------------------------------------------------- + +1) Add this plugin to lib/settings.yml. + See compress.rb for instructions. diff --git a/vendor/assets/stylesheets/blueprint/plugins/fancy-type/screen.css b/vendor/assets/stylesheets/blueprint/plugins/fancy-type/screen.css new file mode 100644 index 00000000000..127cf2553d8 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/fancy-type/screen.css @@ -0,0 +1,71 @@ +/* -------------------------------------------------------------- + + fancy-type.css + * Lots of pretty advanced classes for manipulating text. + + See the Readme file in this folder for additional instructions. + +-------------------------------------------------------------- */ + +/* Indentation instead of line shifts for sibling paragraphs. */ + p + p { text-indent:2em; margin-top:-1.5em; } + form p + p { text-indent: 0; } /* Don't want this in forms. */ + + +/* For great looking type, use this code instead of asdf: + asdf + Best used on prepositions and ampersands. */ + +.alt { + color: #666; + font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; + font-style: italic; + font-weight: normal; +} + + +/* For great looking quote marks in titles, replace "asdf" with: + asdf” + (That is, when the title starts with a quote mark). + (You may have to change this value depending on your font size). */ + +.dquo { margin-left: -.5em; } + + +/* Reduced size type with incremental leading + (http://www.markboulton.co.uk/journal/comments/incremental_leading/) + + This could be used for side notes. For smaller type, you don't necessarily want to + follow the 1.5x vertical rhythm -- the line-height is too much. + + Using this class, it reduces your font size and line-height so that for + every four lines of normal sized type, there is five lines of the sidenote. eg: + + New type size in em's: + 10px (wanted side note size) / 12px (existing base size) = 0.8333 (new type size in ems) + + New line-height value: + 12px x 1.5 = 18px (old line-height) + 18px x 4 = 72px + 72px / 5 = 14.4px (new line height) + 14.4px / 10px = 1.44 (new line height in em's) */ + +p.incr, .incr p { + font-size: 10px; + line-height: 1.44em; + margin-bottom: 1.5em; +} + + +/* Surround uppercase words and abbreviations with this class. + Based on work by Jørgen Arnor Gårdsø Lom [http://twistedintellect.com/] */ + +.caps { + font-variant: small-caps; + letter-spacing: 1px; + text-transform: lowercase; + font-size:1.2em; + line-height:1%; + font-weight:bold; + padding:0 2px; +} diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/doc.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/doc.png new file mode 100644 index 00000000000..834cdfaf48a Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/doc.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/email.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/email.png new file mode 100644 index 00000000000..7348aed77fe Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/email.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/external.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/external.png new file mode 100644 index 00000000000..cf1cfb42687 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/external.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/feed.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/feed.png new file mode 100644 index 00000000000..315c4f4fa62 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/feed.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/im.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/im.png new file mode 100644 index 00000000000..79f35ccbdad Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/im.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/lock.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/lock.png new file mode 100644 index 00000000000..2ebc4f6f966 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/lock.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/pdf.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/pdf.png new file mode 100644 index 00000000000..8f8095e46fa Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/pdf.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/visited.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/visited.png new file mode 100644 index 00000000000..ebf206def27 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/visited.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/xls.png b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/xls.png new file mode 100644 index 00000000000..b977d7e52e2 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/plugins/link-icons/icons/xls.png differ diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/readme.txt b/vendor/assets/stylesheets/blueprint/plugins/link-icons/readme.txt new file mode 100644 index 00000000000..fc4dc649002 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/link-icons/readme.txt @@ -0,0 +1,18 @@ +Link Icons +* Icons for links based on protocol or file type. + +This is not supported in IE versions < 7. + + +Credits +---------------------------------------------------------------- + +* Marc Morgan +* Olav Bjorkoy [bjorkoy.com] + + +Usage +---------------------------------------------------------------- + +1) Add this line to your HTML: + diff --git a/vendor/assets/stylesheets/blueprint/plugins/link-icons/screen.css b/vendor/assets/stylesheets/blueprint/plugins/link-icons/screen.css new file mode 100644 index 00000000000..0cefc774250 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/link-icons/screen.css @@ -0,0 +1,42 @@ +/* -------------------------------------------------------------- + + link-icons.css + * Icons for links based on protocol or file type. + + See the Readme file in this folder for additional instructions. + +-------------------------------------------------------------- */ + +/* Use this class if a link gets an icon when it shouldn't. */ +body a.noicon { + background:transparent none !important; + padding:0 !important; + margin:0 !important; +} + +/* Make sure the icons are not cut */ +a[href^="http:"], a[href^="https:"], +a[href^="http:"]:visited, a[href^="https:"]:visited, +a[href^="mailto:"], a[href$=".pdf"], a[href$=".doc"], a[href$=".xls"], +a[href$=".rss"], a[href$=".rdf"], a[href^="aim:"] { + padding:2px 22px 2px 0; + margin:-2px 0; + background-repeat: no-repeat; + background-position: right center; +} + +/* External links */ +a[href^="http:"] { background-image: url(icons/external.png); } +a[href^="https:"] { background-image: url(icons/lock.png); } +a[href^="mailto:"] { background-image: url(icons/email.png); } +a[href^="http:"]:visited { background-image: url(icons/visited.png); } + +/* Files */ +a[href$=".pdf"] { background-image: url(icons/pdf.png); } +a[href$=".doc"] { background-image: url(icons/doc.png); } +a[href$=".xls"] { background-image: url(icons/xls.png); } + +/* Misc */ +a[href$=".rss"], +a[href$=".rdf"] { background-image: url(icons/feed.png); } +a[href^="aim:"] { background-image: url(icons/im.png); } diff --git a/vendor/assets/stylesheets/blueprint/plugins/rtl/readme.txt b/vendor/assets/stylesheets/blueprint/plugins/rtl/readme.txt new file mode 100644 index 00000000000..5564c402ad0 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/rtl/readme.txt @@ -0,0 +1,10 @@ +RTL +* Mirrors Blueprint, so it can be used with Right-to-Left languages. + +By Ran Yaniv Hartstein, ranh.co.il + +Usage +---------------------------------------------------------------- + +1) Add this line to your HTML: + diff --git a/vendor/assets/stylesheets/blueprint/plugins/rtl/screen.css b/vendor/assets/stylesheets/blueprint/plugins/rtl/screen.css new file mode 100644 index 00000000000..7db7eb5e62f --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/plugins/rtl/screen.css @@ -0,0 +1,110 @@ +/* -------------------------------------------------------------- + + rtl.css + * Mirrors Blueprint for left-to-right languages + + By Ran Yaniv Hartstein [ranh.co.il] + +-------------------------------------------------------------- */ + +body .container { direction: rtl; } +body .column, body .span-1, body .span-2, body .span-3, body .span-4, body .span-5, body .span-6, body .span-7, body .span-8, body .span-9, body .span-10, body .span-11, body .span-12, body .span-13, body .span-14, body .span-15, body .span-16, body .span-17, body .span-18, body .span-19, body .span-20, body .span-21, body .span-22, body .span-23, body .span-24 { + float: right; + margin-right: 0; + margin-left: 10px; + text-align:right; +} + +body div.last { margin-left: 0; } +body table .last { padding-left: 0; } + +body .append-1 { padding-right: 0; padding-left: 40px; } +body .append-2 { padding-right: 0; padding-left: 80px; } +body .append-3 { padding-right: 0; padding-left: 120px; } +body .append-4 { padding-right: 0; padding-left: 160px; } +body .append-5 { padding-right: 0; padding-left: 200px; } +body .append-6 { padding-right: 0; padding-left: 240px; } +body .append-7 { padding-right: 0; padding-left: 280px; } +body .append-8 { padding-right: 0; padding-left: 320px; } +body .append-9 { padding-right: 0; padding-left: 360px; } +body .append-10 { padding-right: 0; padding-left: 400px; } +body .append-11 { padding-right: 0; padding-left: 440px; } +body .append-12 { padding-right: 0; padding-left: 480px; } +body .append-13 { padding-right: 0; padding-left: 520px; } +body .append-14 { padding-right: 0; padding-left: 560px; } +body .append-15 { padding-right: 0; padding-left: 600px; } +body .append-16 { padding-right: 0; padding-left: 640px; } +body .append-17 { padding-right: 0; padding-left: 680px; } +body .append-18 { padding-right: 0; padding-left: 720px; } +body .append-19 { padding-right: 0; padding-left: 760px; } +body .append-20 { padding-right: 0; padding-left: 800px; } +body .append-21 { padding-right: 0; padding-left: 840px; } +body .append-22 { padding-right: 0; padding-left: 880px; } +body .append-23 { padding-right: 0; padding-left: 920px; } + +body .prepend-1 { padding-left: 0; padding-right: 40px; } +body .prepend-2 { padding-left: 0; padding-right: 80px; } +body .prepend-3 { padding-left: 0; padding-right: 120px; } +body .prepend-4 { padding-left: 0; padding-right: 160px; } +body .prepend-5 { padding-left: 0; padding-right: 200px; } +body .prepend-6 { padding-left: 0; padding-right: 240px; } +body .prepend-7 { padding-left: 0; padding-right: 280px; } +body .prepend-8 { padding-left: 0; padding-right: 320px; } +body .prepend-9 { padding-left: 0; padding-right: 360px; } +body .prepend-10 { padding-left: 0; padding-right: 400px; } +body .prepend-11 { padding-left: 0; padding-right: 440px; } +body .prepend-12 { padding-left: 0; padding-right: 480px; } +body .prepend-13 { padding-left: 0; padding-right: 520px; } +body .prepend-14 { padding-left: 0; padding-right: 560px; } +body .prepend-15 { padding-left: 0; padding-right: 600px; } +body .prepend-16 { padding-left: 0; padding-right: 640px; } +body .prepend-17 { padding-left: 0; padding-right: 680px; } +body .prepend-18 { padding-left: 0; padding-right: 720px; } +body .prepend-19 { padding-left: 0; padding-right: 760px; } +body .prepend-20 { padding-left: 0; padding-right: 800px; } +body .prepend-21 { padding-left: 0; padding-right: 840px; } +body .prepend-22 { padding-left: 0; padding-right: 880px; } +body .prepend-23 { padding-left: 0; padding-right: 920px; } + +body .border { + padding-right: 0; + padding-left: 4px; + margin-right: 0; + margin-left: 5px; + border-right: none; + border-left: 1px solid #eee; +} + +body .colborder { + padding-right: 0; + padding-left: 24px; + margin-right: 0; + margin-left: 25px; + border-right: none; + border-left: 1px solid #eee; +} + +body .pull-1 { margin-left: 0; margin-right: -40px; } +body .pull-2 { margin-left: 0; margin-right: -80px; } +body .pull-3 { margin-left: 0; margin-right: -120px; } +body .pull-4 { margin-left: 0; margin-right: -160px; } + +body .push-0 { margin: 0 18px 0 0; } +body .push-1 { margin: 0 18px 0 -40px; } +body .push-2 { margin: 0 18px 0 -80px; } +body .push-3 { margin: 0 18px 0 -120px; } +body .push-4 { margin: 0 18px 0 -160px; } +body .push-0, body .push-1, body .push-2, +body .push-3, body .push-4 { float: left; } + + +/* Typography with RTL support */ +body h1,body h2,body h3, +body h4,body h5,body h6 { font-family: Arial, sans-serif; } +html body { font-family: Arial, sans-serif; } +body pre,body code,body tt { font-family: monospace; } + +/* Mirror floats and margins on typographic elements */ +body p img { float: right; margin: 1.5em 0 1.5em 1.5em; } +body dd, body ul, body ol { margin-left: 0; margin-right: 1.5em;} +body td, body th { text-align:right; } diff --git a/vendor/assets/stylesheets/blueprint/print.css b/vendor/assets/stylesheets/blueprint/print.css new file mode 100644 index 00000000000..bd79afdea55 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/print.css @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* print.css */ +body {line-height:1.5;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;color:#000;background:none;font-size:10pt;} +.container {background:none;} +hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} +hr.space {background:#fff;color:#fff;visibility:hidden;} +h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} +code {font:.9em "Courier New", Monaco, Courier, monospace;} +a img {border:none;} +p img.top {margin-top:0;} +blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} +.small {font-size:.9em;} +.large {font-size:1.1em;} +.quiet {color:#999;} +.hide {display:none;} +a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} +a:link:after, a:visited:after {content:" (" attr(href) ")";font-size:90%;} \ No newline at end of file diff --git a/vendor/assets/stylesheets/blueprint/screen.css b/vendor/assets/stylesheets/blueprint/screen.css new file mode 100644 index 00000000000..fe68de6e4fc --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/screen.css @@ -0,0 +1,265 @@ +/* ----------------------------------------------------------------------- + + + Blueprint CSS Framework 1.0.1 + http://blueprintcss.org + + * Copyright (c) 2007-Present. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* reset.css */ +html {margin:0;padding:0;border:0;} +body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section {margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;} +article, aside, details, figcaption, figure, dialog, footer, header, hgroup, menu, nav, section {display:block;} +body {line-height:1.5;background:white;} +table {border-collapse:separate;border-spacing:0;} +caption, th, td {text-align:left;font-weight:normal;float:none !important;} +table, th, td {vertical-align:middle;} +blockquote:before, blockquote:after, q:before, q:after {content:'';} +blockquote, q {quotes:"" "";} +a img {border:none;} +:focus {outline:0;} + +/* typography.css */ +html {font-size:100.01%;} +body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} +h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} +h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} +h2 {font-size:2em;margin-bottom:0.75em;} +h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} +h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} +h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} +h6 {font-size:1em;font-weight:bold;} +h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} +p {margin:0 0 1.5em;} +.left {float:left !important;} +p .left {margin:1.5em 1.5em 1.5em 0;padding:0;} +.right {float:right !important;} +p .right {margin:1.5em 0 1.5em 1.5em;padding:0;} +a:focus, a:hover {color:#09f;} +a {color:#06c;text-decoration:underline;} +blockquote {margin:1.5em;color:#666;font-style:italic;} +strong, dfn {font-weight:bold;} +em, dfn {font-style:italic;} +sup, sub {line-height:0;} +abbr, acronym {border-bottom:1px dotted #666;} +address {margin:0 0 1.5em;font-style:italic;} +del {color:#666;} +pre {margin:1.5em 0;white-space:pre;} +pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} +li ul, li ol {margin:0;} +ul, ol {margin:0 1.5em 1.5em 0;padding-left:1.5em;} +ul {list-style-type:disc;} +ol {list-style-type:decimal;} +dl {margin:0 0 1.5em 0;} +dl dt {font-weight:bold;} +dd {margin-left:1.5em;} +table {margin-bottom:1.4em;width:100%;} +th {font-weight:bold;} +thead th {background:#c3d9ff;} +th, td, caption {padding:4px 10px 4px 5px;} +tbody tr:nth-child(even) td, tbody tr.even td {background:#e5ecf9;} +tfoot {font-style:italic;} +caption {background:#eee;} +.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} +.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} +.hide {display:none;} +.quiet {color:#666;} +.loud {color:#000;} +.highlight {background:#ff0;} +.added {background:#060;color:#fff;} +.removed {background:#900;color:#fff;} +.first {margin-left:0;padding-left:0;} +.last {margin-right:0;padding-right:0;} +.top {margin-top:0;padding-top:0;} +.bottom {margin-bottom:0;padding-bottom:0;} + +/* forms.css */ +label {font-weight:bold;} +fieldset {padding:0 1.4em 1.4em 1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} +legend {font-weight:bold;font-size:1.2em;margin-top:-0.2em;margin-bottom:1em;} +fieldset, #IE8#HACK {padding-top:1.4em;} +legend, #IE8#HACK {margin-top:0;margin-bottom:0;} +input[type=text], input[type=password], input[type=url], input[type=email], input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;color:#000;} +input[type=text]:focus, input[type=password]:focus, input[type=url]:focus, input[type=email]:focus, input.text:focus, input.title:focus, textarea:focus {border-color:#666;} +select {background-color:#fff;border-width:1px;border-style:solid;} +input[type=text], input[type=password], input[type=url], input[type=email], input.text, input.title, textarea, select {margin:0.5em 0;} +input.text, input.title {width:300px;padding:5px;} +input.title {font-size:1.5em;} +textarea {width:390px;height:250px;padding:5px;} +form.inline {line-height:3;} +form.inline p {margin-bottom:0;} +.error, .alert, .notice, .success, .info {padding:0.8em;margin-bottom:1em;border:2px solid #ddd;} +.error, .alert {background:#fbe3e4;color:#8a1f11;border-color:#fbc2c4;} +.notice {background:#fff6bf;color:#514721;border-color:#ffd324;} +.success {background:#e6efc2;color:#264409;border-color:#c6d880;} +.info {background:#d5edf8;color:#205791;border-color:#92cae4;} +.error a, .alert a {color:#8a1f11;} +.notice a {color:#514721;} +.success a {color:#264409;} +.info a {color:#205791;} + +/* grid.css */ +.container {width:950px;margin:0 auto;} +.showgrid {background:url(src/grid.png);} +.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 {float:left;margin-right:10px;} +.last {margin-right:0;} +.span-1 {width:30px;} +.span-2 {width:70px;} +.span-3 {width:110px;} +.span-4 {width:150px;} +.span-5 {width:190px;} +.span-6 {width:230px;} +.span-7 {width:270px;} +.span-8 {width:310px;} +.span-9 {width:350px;} +.span-10 {width:390px;} +.span-11 {width:430px;} +.span-12 {width:470px;} +.span-13 {width:510px;} +.span-14 {width:550px;} +.span-15 {width:590px;} +.span-16 {width:630px;} +.span-17 {width:670px;} +.span-18 {width:710px;} +.span-19 {width:750px;} +.span-20 {width:790px;} +.span-21 {width:830px;} +.span-22 {width:870px;} +.span-23 {width:910px;} +.span-24 {width:950px;margin-right:0;} +input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px;border-right-width:1px;padding-left:5px;padding-right:5px;} +input.span-1, textarea.span-1 {width:18px;} +input.span-2, textarea.span-2 {width:58px;} +input.span-3, textarea.span-3 {width:98px;} +input.span-4, textarea.span-4 {width:138px;} +input.span-5, textarea.span-5 {width:178px;} +input.span-6, textarea.span-6 {width:218px;} +input.span-7, textarea.span-7 {width:258px;} +input.span-8, textarea.span-8 {width:298px;} +input.span-9, textarea.span-9 {width:338px;} +input.span-10, textarea.span-10 {width:378px;} +input.span-11, textarea.span-11 {width:418px;} +input.span-12, textarea.span-12 {width:458px;} +input.span-13, textarea.span-13 {width:498px;} +input.span-14, textarea.span-14 {width:538px;} +input.span-15, textarea.span-15 {width:578px;} +input.span-16, textarea.span-16 {width:618px;} +input.span-17, textarea.span-17 {width:658px;} +input.span-18, textarea.span-18 {width:698px;} +input.span-19, textarea.span-19 {width:738px;} +input.span-20, textarea.span-20 {width:778px;} +input.span-21, textarea.span-21 {width:818px;} +input.span-22, textarea.span-22 {width:858px;} +input.span-23, textarea.span-23 {width:898px;} +input.span-24, textarea.span-24 {width:938px;} +.append-1 {padding-right:40px;} +.append-2 {padding-right:80px;} +.append-3 {padding-right:120px;} +.append-4 {padding-right:160px;} +.append-5 {padding-right:200px;} +.append-6 {padding-right:240px;} +.append-7 {padding-right:280px;} +.append-8 {padding-right:320px;} +.append-9 {padding-right:360px;} +.append-10 {padding-right:400px;} +.append-11 {padding-right:440px;} +.append-12 {padding-right:480px;} +.append-13 {padding-right:520px;} +.append-14 {padding-right:560px;} +.append-15 {padding-right:600px;} +.append-16 {padding-right:640px;} +.append-17 {padding-right:680px;} +.append-18 {padding-right:720px;} +.append-19 {padding-right:760px;} +.append-20 {padding-right:800px;} +.append-21 {padding-right:840px;} +.append-22 {padding-right:880px;} +.append-23 {padding-right:920px;} +.prepend-1 {padding-left:40px;} +.prepend-2 {padding-left:80px;} +.prepend-3 {padding-left:120px;} +.prepend-4 {padding-left:160px;} +.prepend-5 {padding-left:200px;} +.prepend-6 {padding-left:240px;} +.prepend-7 {padding-left:280px;} +.prepend-8 {padding-left:320px;} +.prepend-9 {padding-left:360px;} +.prepend-10 {padding-left:400px;} +.prepend-11 {padding-left:440px;} +.prepend-12 {padding-left:480px;} +.prepend-13 {padding-left:520px;} +.prepend-14 {padding-left:560px;} +.prepend-15 {padding-left:600px;} +.prepend-16 {padding-left:640px;} +.prepend-17 {padding-left:680px;} +.prepend-18 {padding-left:720px;} +.prepend-19 {padding-left:760px;} +.prepend-20 {padding-left:800px;} +.prepend-21 {padding-left:840px;} +.prepend-22 {padding-left:880px;} +.prepend-23 {padding-left:920px;} +.border {padding-right:4px;margin-right:5px;border-right:1px solid #ddd;} +.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #ddd;} +.pull-1 {margin-left:-40px;} +.pull-2 {margin-left:-80px;} +.pull-3 {margin-left:-120px;} +.pull-4 {margin-left:-160px;} +.pull-5 {margin-left:-200px;} +.pull-6 {margin-left:-240px;} +.pull-7 {margin-left:-280px;} +.pull-8 {margin-left:-320px;} +.pull-9 {margin-left:-360px;} +.pull-10 {margin-left:-400px;} +.pull-11 {margin-left:-440px;} +.pull-12 {margin-left:-480px;} +.pull-13 {margin-left:-520px;} +.pull-14 {margin-left:-560px;} +.pull-15 {margin-left:-600px;} +.pull-16 {margin-left:-640px;} +.pull-17 {margin-left:-680px;} +.pull-18 {margin-left:-720px;} +.pull-19 {margin-left:-760px;} +.pull-20 {margin-left:-800px;} +.pull-21 {margin-left:-840px;} +.pull-22 {margin-left:-880px;} +.pull-23 {margin-left:-920px;} +.pull-24 {margin-left:-960px;} +.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} +.push-1 {margin:0 -40px 1.5em 40px;} +.push-2 {margin:0 -80px 1.5em 80px;} +.push-3 {margin:0 -120px 1.5em 120px;} +.push-4 {margin:0 -160px 1.5em 160px;} +.push-5 {margin:0 -200px 1.5em 200px;} +.push-6 {margin:0 -240px 1.5em 240px;} +.push-7 {margin:0 -280px 1.5em 280px;} +.push-8 {margin:0 -320px 1.5em 320px;} +.push-9 {margin:0 -360px 1.5em 360px;} +.push-10 {margin:0 -400px 1.5em 400px;} +.push-11 {margin:0 -440px 1.5em 440px;} +.push-12 {margin:0 -480px 1.5em 480px;} +.push-13 {margin:0 -520px 1.5em 520px;} +.push-14 {margin:0 -560px 1.5em 560px;} +.push-15 {margin:0 -600px 1.5em 600px;} +.push-16 {margin:0 -640px 1.5em 640px;} +.push-17 {margin:0 -680px 1.5em 680px;} +.push-18 {margin:0 -720px 1.5em 720px;} +.push-19 {margin:0 -760px 1.5em 760px;} +.push-20 {margin:0 -800px 1.5em 800px;} +.push-21 {margin:0 -840px 1.5em 840px;} +.push-22 {margin:0 -880px 1.5em 880px;} +.push-23 {margin:0 -920px 1.5em 920px;} +.push-24 {margin:0 -960px 1.5em 960px;} +.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:left;position:relative;} +div.prepend-top, .prepend-top {margin-top:1.5em;} +div.append-bottom, .append-bottom {margin-bottom:1.5em;} +.box {padding:1.5em;margin-bottom:1.5em;background:#e5eCf9;} +hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:1px;margin:0 0 17px;border:none;} +hr.space {background:#fff;color:#fff;visibility:hidden;} +.clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} +.clearfix, .container {display:block;} +.clear {clear:both;} \ No newline at end of file diff --git a/vendor/assets/stylesheets/blueprint/src/forms.css b/vendor/assets/stylesheets/blueprint/src/forms.css new file mode 100644 index 00000000000..7ceb966e242 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/forms.css @@ -0,0 +1,82 @@ +/* -------------------------------------------------------------- + + forms.css + * Sets up some default styling for forms + * Gives you classes to enhance your forms + + Usage: + * For text fields, use class .title or .text + * For inline forms, use .inline (even when using columns) + +-------------------------------------------------------------- */ + +/* + A special hack is included for IE8 since it does not apply padding + correctly on fieldsets + */ +label { font-weight: bold; } +fieldset { padding:0 1.4em 1.4em 1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; } +legend { font-weight: bold; font-size:1.2em; margin-top:-0.2em; margin-bottom:1em; } + +fieldset, #IE8#HACK { padding-top:1.4em; } +legend, #IE8#HACK { margin-top:0; margin-bottom:0; } + +/* Form fields +-------------------------------------------------------------- */ + +/* + Attribute selectors are used to differentiate the different types + of input elements, but to support old browsers, you will have to + add classes for each one. ".title" simply creates a large text + field, this is purely for looks. + */ +input[type=text], input[type=password], input[type=url], input[type=email], +input.text, input.title, +textarea { + background-color:#fff; + border:1px solid #bbb; + color:#000; +} +input[type=text]:focus, input[type=password]:focus, input[type=url]:focus, input[type=email]:focus, +input.text:focus, input.title:focus, +textarea:focus { + border-color:#666; +} +select { background-color:#fff; border-width:1px; border-style:solid; } + +input[type=text], input[type=password], input[type=url], input[type=email], +input.text, input.title, +textarea, select { + margin:0.5em 0; +} + +input.text, +input.title { width: 300px; padding:5px; } +input.title { font-size:1.5em; } +textarea { width: 390px; height: 250px; padding:5px; } + +/* + This is to be used on forms where a variety of elements are + placed side-by-side. Use the p tag to denote a line. + */ +form.inline { line-height:3; } +form.inline p { margin-bottom:0; } + + +/* Success, info, notice and error/alert boxes +-------------------------------------------------------------- */ + +.error, +.alert, +.notice, +.success, +.info { padding: 0.8em; margin-bottom: 1em; border: 2px solid #ddd; } + +.error, .alert { background: #fbe3e4; color: #8a1f11; border-color: #fbc2c4; } +.notice { background: #fff6bf; color: #514721; border-color: #ffd324; } +.success { background: #e6efc2; color: #264409; border-color: #c6d880; } +.info { background: #d5edf8; color: #205791; border-color: #92cae4; } +.error a, .alert a { color: #8a1f11; } +.notice a { color: #514721; } +.success a { color: #264409; } +.info a { color: #205791; } diff --git a/vendor/assets/stylesheets/blueprint/src/grid.css b/vendor/assets/stylesheets/blueprint/src/grid.css new file mode 100755 index 00000000000..dbd5738130e --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/grid.css @@ -0,0 +1,280 @@ +/* -------------------------------------------------------------- + + grid.css + * Sets up an easy-to-use grid of 24 columns. + + By default, the grid is 950px wide, with 24 columns + spanning 30px, and a 10px margin between columns. + + If you need fewer or more columns, namespaces or semantic + element names, use the compressor script (lib/compress.rb) + +-------------------------------------------------------------- */ + +/* A container should group all your columns. */ +.container { + width: 950px; + margin: 0 auto; +} + +/* Use this class on any .span / container to see the grid. */ +.showgrid { + background: url(src/grid.png); +} + + +/* Columns +-------------------------------------------------------------- */ + +/* Sets up basic grid floating and margin. */ +.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { + float: left; + margin-right: 10px; +} + +/* The last column in a row needs this class. */ +.last { margin-right: 0; } + +/* Use these classes to set the width of a column. */ +.span-1 {width: 30px;} + +.span-2 {width: 70px;} +.span-3 {width: 110px;} +.span-4 {width: 150px;} +.span-5 {width: 190px;} +.span-6 {width: 230px;} +.span-7 {width: 270px;} +.span-8 {width: 310px;} +.span-9 {width: 350px;} +.span-10 {width: 390px;} +.span-11 {width: 430px;} +.span-12 {width: 470px;} +.span-13 {width: 510px;} +.span-14 {width: 550px;} +.span-15 {width: 590px;} +.span-16 {width: 630px;} +.span-17 {width: 670px;} +.span-18 {width: 710px;} +.span-19 {width: 750px;} +.span-20 {width: 790px;} +.span-21 {width: 830px;} +.span-22 {width: 870px;} +.span-23 {width: 910px;} +.span-24 {width:950px; margin-right:0;} + +/* Use these classes to set the width of an input. */ +input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 { + border-left-width: 1px; + border-right-width: 1px; + padding-left: 5px; + padding-right: 5px; +} + +input.span-1, textarea.span-1 { width: 18px; } +input.span-2, textarea.span-2 { width: 58px; } +input.span-3, textarea.span-3 { width: 98px; } +input.span-4, textarea.span-4 { width: 138px; } +input.span-5, textarea.span-5 { width: 178px; } +input.span-6, textarea.span-6 { width: 218px; } +input.span-7, textarea.span-7 { width: 258px; } +input.span-8, textarea.span-8 { width: 298px; } +input.span-9, textarea.span-9 { width: 338px; } +input.span-10, textarea.span-10 { width: 378px; } +input.span-11, textarea.span-11 { width: 418px; } +input.span-12, textarea.span-12 { width: 458px; } +input.span-13, textarea.span-13 { width: 498px; } +input.span-14, textarea.span-14 { width: 538px; } +input.span-15, textarea.span-15 { width: 578px; } +input.span-16, textarea.span-16 { width: 618px; } +input.span-17, textarea.span-17 { width: 658px; } +input.span-18, textarea.span-18 { width: 698px; } +input.span-19, textarea.span-19 { width: 738px; } +input.span-20, textarea.span-20 { width: 778px; } +input.span-21, textarea.span-21 { width: 818px; } +input.span-22, textarea.span-22 { width: 858px; } +input.span-23, textarea.span-23 { width: 898px; } +input.span-24, textarea.span-24 { width: 938px; } + +/* Add these to a column to append empty cols. */ + +.append-1 { padding-right: 40px;} +.append-2 { padding-right: 80px;} +.append-3 { padding-right: 120px;} +.append-4 { padding-right: 160px;} +.append-5 { padding-right: 200px;} +.append-6 { padding-right: 240px;} +.append-7 { padding-right: 280px;} +.append-8 { padding-right: 320px;} +.append-9 { padding-right: 360px;} +.append-10 { padding-right: 400px;} +.append-11 { padding-right: 440px;} +.append-12 { padding-right: 480px;} +.append-13 { padding-right: 520px;} +.append-14 { padding-right: 560px;} +.append-15 { padding-right: 600px;} +.append-16 { padding-right: 640px;} +.append-17 { padding-right: 680px;} +.append-18 { padding-right: 720px;} +.append-19 { padding-right: 760px;} +.append-20 { padding-right: 800px;} +.append-21 { padding-right: 840px;} +.append-22 { padding-right: 880px;} +.append-23 { padding-right: 920px;} + +/* Add these to a column to prepend empty cols. */ + +.prepend-1 { padding-left: 40px;} +.prepend-2 { padding-left: 80px;} +.prepend-3 { padding-left: 120px;} +.prepend-4 { padding-left: 160px;} +.prepend-5 { padding-left: 200px;} +.prepend-6 { padding-left: 240px;} +.prepend-7 { padding-left: 280px;} +.prepend-8 { padding-left: 320px;} +.prepend-9 { padding-left: 360px;} +.prepend-10 { padding-left: 400px;} +.prepend-11 { padding-left: 440px;} +.prepend-12 { padding-left: 480px;} +.prepend-13 { padding-left: 520px;} +.prepend-14 { padding-left: 560px;} +.prepend-15 { padding-left: 600px;} +.prepend-16 { padding-left: 640px;} +.prepend-17 { padding-left: 680px;} +.prepend-18 { padding-left: 720px;} +.prepend-19 { padding-left: 760px;} +.prepend-20 { padding-left: 800px;} +.prepend-21 { padding-left: 840px;} +.prepend-22 { padding-left: 880px;} +.prepend-23 { padding-left: 920px;} + + +/* Border on right hand side of a column. */ +.border { + padding-right: 4px; + margin-right: 5px; + border-right: 1px solid #ddd; +} + +/* Border with more whitespace, spans one column. */ +.colborder { + padding-right: 24px; + margin-right: 25px; + border-right: 1px solid #ddd; +} + + +/* Use these classes on an element to push it into the +next column, or to pull it into the previous column. */ + + +.pull-1 { margin-left: -40px; } +.pull-2 { margin-left: -80px; } +.pull-3 { margin-left: -120px; } +.pull-4 { margin-left: -160px; } +.pull-5 { margin-left: -200px; } +.pull-6 { margin-left: -240px; } +.pull-7 { margin-left: -280px; } +.pull-8 { margin-left: -320px; } +.pull-9 { margin-left: -360px; } +.pull-10 { margin-left: -400px; } +.pull-11 { margin-left: -440px; } +.pull-12 { margin-left: -480px; } +.pull-13 { margin-left: -520px; } +.pull-14 { margin-left: -560px; } +.pull-15 { margin-left: -600px; } +.pull-16 { margin-left: -640px; } +.pull-17 { margin-left: -680px; } +.pull-18 { margin-left: -720px; } +.pull-19 { margin-left: -760px; } +.pull-20 { margin-left: -800px; } +.pull-21 { margin-left: -840px; } +.pull-22 { margin-left: -880px; } +.pull-23 { margin-left: -920px; } +.pull-24 { margin-left: -960px; } + +.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float: left; position:relative;} + + +.push-1 { margin: 0 -40px 1.5em 40px; } +.push-2 { margin: 0 -80px 1.5em 80px; } +.push-3 { margin: 0 -120px 1.5em 120px; } +.push-4 { margin: 0 -160px 1.5em 160px; } +.push-5 { margin: 0 -200px 1.5em 200px; } +.push-6 { margin: 0 -240px 1.5em 240px; } +.push-7 { margin: 0 -280px 1.5em 280px; } +.push-8 { margin: 0 -320px 1.5em 320px; } +.push-9 { margin: 0 -360px 1.5em 360px; } +.push-10 { margin: 0 -400px 1.5em 400px; } +.push-11 { margin: 0 -440px 1.5em 440px; } +.push-12 { margin: 0 -480px 1.5em 480px; } +.push-13 { margin: 0 -520px 1.5em 520px; } +.push-14 { margin: 0 -560px 1.5em 560px; } +.push-15 { margin: 0 -600px 1.5em 600px; } +.push-16 { margin: 0 -640px 1.5em 640px; } +.push-17 { margin: 0 -680px 1.5em 680px; } +.push-18 { margin: 0 -720px 1.5em 720px; } +.push-19 { margin: 0 -760px 1.5em 760px; } +.push-20 { margin: 0 -800px 1.5em 800px; } +.push-21 { margin: 0 -840px 1.5em 840px; } +.push-22 { margin: 0 -880px 1.5em 880px; } +.push-23 { margin: 0 -920px 1.5em 920px; } +.push-24 { margin: 0 -960px 1.5em 960px; } + +.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float: left; position:relative;} + + +/* Misc classes and elements +-------------------------------------------------------------- */ + +/* In case you need to add a gutter above/below an element */ +div.prepend-top, .prepend-top { + margin-top:1.5em; +} +div.append-bottom, .append-bottom { + margin-bottom:1.5em; +} + +/* Use a .box to create a padded box inside a column. */ +.box { + padding: 1.5em; + margin-bottom: 1.5em; + background: #e5eCf9; +} + +/* Use this to create a horizontal ruler across a column. */ +hr { + background: #ddd; + color: #ddd; + clear: both; + float: none; + width: 100%; + height: 1px; + margin: 0 0 17px; + border: none; +} + +hr.space { + background: #fff; + color: #fff; + visibility: hidden; +} + + +/* Clearing floats without extra markup + Based on How To Clear Floats Without Structural Markup by PiE + [http://www.positioniseverything.net/easyclearing.html] */ + +.clearfix:after, .container:after { + content: "\0020"; + display: block; + height: 0; + clear: both; + visibility: hidden; + overflow:hidden; +} +.clearfix, .container {display: block;} + +/* Regular clearing + apply to column that should drop below previous ones. */ + +.clear { clear:both; } diff --git a/vendor/assets/stylesheets/blueprint/src/grid.png b/vendor/assets/stylesheets/blueprint/src/grid.png new file mode 100644 index 00000000000..d42a6c32c17 Binary files /dev/null and b/vendor/assets/stylesheets/blueprint/src/grid.png differ diff --git a/vendor/assets/stylesheets/blueprint/src/ie.css b/vendor/assets/stylesheets/blueprint/src/ie.css new file mode 100644 index 00000000000..111a2eaf2e0 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/ie.css @@ -0,0 +1,79 @@ +/* -------------------------------------------------------------- + + ie.css + + Contains every hack for Internet Explorer, + so that our core files stay sweet and nimble. + +-------------------------------------------------------------- */ + +/* Make sure the layout is centered in IE5 */ +body { text-align: center; } +.container { text-align: left; } + +/* Fixes IE margin bugs */ +* html .column, * html .span-1, * html .span-2, +* html .span-3, * html .span-4, * html .span-5, +* html .span-6, * html .span-7, * html .span-8, +* html .span-9, * html .span-10, * html .span-11, +* html .span-12, * html .span-13, * html .span-14, +* html .span-15, * html .span-16, * html .span-17, +* html .span-18, * html .span-19, * html .span-20, +* html .span-21, * html .span-22, * html .span-23, +* html .span-24 { display:inline; overflow-x: hidden; } + + +/* Elements +-------------------------------------------------------------- */ + +/* Fixes incorrect styling of legend in IE6. */ +* html legend { margin:0px -8px 16px 0; padding:0; } + +/* Fixes wrong line-height on sup/sub in IE. */ +sup { vertical-align:text-top; } +sub { vertical-align:text-bottom; } + +/* Fixes IE7 missing wrapping of code elements. */ +html>body p code { *white-space: normal; } + +/* IE 6&7 has problems with setting proper
      margins. */ +hr { margin:-8px auto 11px; } + +/* Explicitly set interpolation, allowing dynamically resized images to not look horrible */ +img { -ms-interpolation-mode:bicubic; } + +/* Clearing +-------------------------------------------------------------- */ + +/* Makes clearfix actually work in IE */ +.clearfix, .container { display:inline-block; } +* html .clearfix, +* html .container { height:1%; } + + +/* Forms +-------------------------------------------------------------- */ + +/* Fixes padding on fieldset */ +fieldset { padding-top:0; } +legend { margin-top:-0.2em; margin-bottom:1em; margin-left:-0.5em; } + +/* Makes classic textareas in IE 6 resemble other browsers */ +textarea { overflow:auto; } + +/* Makes labels behave correctly in IE 6 and 7 */ +label { vertical-align:middle; position:relative; top:-0.25em; } + +/* Fixes rule that IE 6 ignores */ +input.text, input.title, textarea { background-color:#fff; border:1px solid #bbb; } +input.text:focus, input.title:focus { border-color:#666; } +input.text, input.title, textarea, select { margin:0.5em 0; } +input.checkbox, input.radio { position:relative; top:.25em; } + +/* Fixes alignment of inline form elements */ +form.inline div, form.inline p { vertical-align:middle; } +form.inline input.checkbox, form.inline input.radio, +form.inline input.button, form.inline button { + margin:0.5em 0; +} +button, input.button { position:relative;top:0.25em; } diff --git a/vendor/assets/stylesheets/blueprint/src/print.css b/vendor/assets/stylesheets/blueprint/src/print.css new file mode 100755 index 00000000000..b230b84282f --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/print.css @@ -0,0 +1,92 @@ +/* -------------------------------------------------------------- + + print.css + * Gives you some sensible styles for printing pages. + * See Readme file in this directory for further instructions. + + Some additions you'll want to make, customized to your markup: + #header, #footer, #navigation { display:none; } + +-------------------------------------------------------------- */ + +body { + line-height: 1.5; + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; + color:#000; + background: none; + font-size: 10pt; +} + + +/* Layout +-------------------------------------------------------------- */ + +.container { + background: none; +} + +hr { + background:#ccc; + color:#ccc; + width:100%; + height:2px; + margin:2em 0; + padding:0; + border:none; +} +hr.space { + background: #fff; + color: #fff; + visibility: hidden; +} + + +/* Text +-------------------------------------------------------------- */ + +h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; } +code { font:.9em "Courier New", Monaco, Courier, monospace; } + +a img { border:none; } +p img.top { margin-top: 0; } + +blockquote { + margin:1.5em; + padding:1em; + font-style:italic; + font-size:.9em; +} + +.small { font-size: .9em; } +.large { font-size: 1.1em; } +.quiet { color: #999; } +.hide { display:none; } + + +/* Links +-------------------------------------------------------------- */ + +a:link, a:visited { + background: transparent; + font-weight:700; + text-decoration: underline; +} + +/* + This has been the source of many questions in the past. This + snippet of CSS appends the URL of each link within the text. + The idea is that users printing your webpage will want to know + the URLs they go to. If you want to remove this functionality, + comment out this snippet and make sure to re-compress your files. + */ +a:link:after, a:visited:after { + content: " (" attr(href) ")"; + font-size: 90%; +} + +/* If you're having trouble printing relative links, uncomment and customize this: + (note: This is valid CSS3, but it still won't go through the W3C CSS Validator) */ + +/* a[href^="/"]:after { + content: " (http://www.yourdomain.com" attr(href) ") "; +} */ diff --git a/vendor/assets/stylesheets/blueprint/src/reset.css b/vendor/assets/stylesheets/blueprint/src/reset.css new file mode 100755 index 00000000000..b26168faa1a --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/reset.css @@ -0,0 +1,65 @@ +/* -------------------------------------------------------------- + + reset.css + * Resets default browser CSS. + +-------------------------------------------------------------- */ + +html { + margin:0; + padding:0; + border:0; +} + +body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, code, +del, dfn, em, img, q, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, dialog, figure, footer, header, +hgroup, nav, section { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* This helps to make newer HTML5 elements behave like DIVs in older browers */ +article, aside, details, figcaption, figure, dialog, +footer, header, hgroup, menu, nav, section { + display:block; +} + +/* Line-height should always be unitless! */ +body { + line-height: 1.5; + background: white; +} + +/* Tables still need 'cellspacing="0"' in the markup. */ +table { + border-collapse: separate; + border-spacing: 0; +} +/* float:none prevents the span-x classes from breaking table-cell display */ +caption, th, td { + text-align: left; + font-weight: normal; + float:none !important; +} +table, th, td { + vertical-align: middle; +} + +/* Remove possible quote marks (") from ,
      . */ +blockquote:before, blockquote:after, q:before, q:after { content: ''; } +blockquote, q { quotes: "" ""; } + +/* Remove annoying border on linked images. */ +a img { border: none; } + +/* Remember to define your own focus styles! */ +:focus { outline: 0; } diff --git a/vendor/assets/stylesheets/blueprint/src/typography.css b/vendor/assets/stylesheets/blueprint/src/typography.css new file mode 100644 index 00000000000..adef7128c79 --- /dev/null +++ b/vendor/assets/stylesheets/blueprint/src/typography.css @@ -0,0 +1,123 @@ +/* -------------------------------------------------------------- + + typography.css + * Sets up some sensible default typography. + +-------------------------------------------------------------- */ + +/* Default font settings. + The font-size percentage is of 16px. (0.75 * 16px = 12px) */ +html { font-size:100.01%; } +body { + font-size: 75%; + color: #222; + background: #fff; + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; +} + + +/* Headings +-------------------------------------------------------------- */ + +h1,h2,h3,h4,h5,h6 { font-weight: normal; color: #111; } + +h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } +h2 { font-size: 2em; margin-bottom: 0.75em; } +h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } +h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; } +h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; } +h6 { font-size: 1em; font-weight: bold; } + +h1 img, h2 img, h3 img, +h4 img, h5 img, h6 img { + margin: 0; +} + + +/* Text elements +-------------------------------------------------------------- */ + +p { margin: 0 0 1.5em; } +/* + These can be used to pull an image at the start of a paragraph, so + that the text flows around it (usage:

      Text

      ) + */ +.left { float: left !important; } +p .left { margin: 1.5em 1.5em 1.5em 0; padding: 0; } +.right { float: right !important; } +p .right { margin: 1.5em 0 1.5em 1.5em; padding: 0; } + +a:focus, +a:hover { color: #09f; } +a { color: #06c; text-decoration: underline; } + +blockquote { margin: 1.5em; color: #666; font-style: italic; } +strong,dfn { font-weight: bold; } +em,dfn { font-style: italic; } +sup, sub { line-height: 0; } + +abbr, +acronym { border-bottom: 1px dotted #666; } +address { margin: 0 0 1.5em; font-style: italic; } +del { color:#666; } + +pre { margin: 1.5em 0; white-space: pre; } +pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } + + +/* Lists +-------------------------------------------------------------- */ + +li ul, +li ol { margin: 0; } +ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; } + +ul { list-style-type: disc; } +ol { list-style-type: decimal; } + +dl { margin: 0 0 1.5em 0; } +dl dt { font-weight: bold; } +dd { margin-left: 1.5em;} + + +/* Tables +-------------------------------------------------------------- */ + +/* + Because of the need for padding on TH and TD, the vertical rhythm + on table cells has to be 27px, instead of the standard 18px or 36px + of other elements. + */ +table { margin-bottom: 1.4em; width:100%; } +th { font-weight: bold; } +thead th { background: #c3d9ff; } +th,td,caption { padding: 4px 10px 4px 5px; } +/* + You can zebra-stripe your tables in outdated browsers by adding + the class "even" to every other table row. + */ +tbody tr:nth-child(even) td, +tbody tr.even td { + background: #e5ecf9; +} +tfoot { font-style: italic; } +caption { background: #eee; } + + +/* Misc classes +-------------------------------------------------------------- */ + +.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; } +.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; } +.hide { display: none; } + +.quiet { color: #666; } +.loud { color: #000; } +.highlight { background:#ff0; } +.added { background:#060; color: #fff; } +.removed { background:#900; color: #fff; } + +.first { margin-left:0; padding-left:0; } +.last { margin-right:0; padding-right:0; } +.top { margin-top:0; padding-top:0; } +.bottom { margin-bottom:0; padding-bottom:0; } diff --git a/vendor/assets/stylesheets/jquery_ui/jquery-ui-1.8.16.custom.css b/vendor/assets/stylesheets/jquery_ui/jquery-ui-1.8.16.custom.css new file mode 100644 index 00000000000..4308e08ea85 --- /dev/null +++ b/vendor/assets/stylesheets/jquery_ui/jquery-ui-1.8.16.custom.css @@ -0,0 +1,577 @@ +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: "Helvetica Neue",Arial,Helvetica,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #dddddd; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +/*.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }*/ +/*.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }*/ +/*.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }*/ +/*.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }*/ + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Resizable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizable#theming + */ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* + * jQuery UI Selectable 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectable#theming + */ +.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } +/* + * jQuery UI Accordion 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion#theming + */ +/* IE/Win - Fix animation bug - #4615 */ +.ui-accordion { width: 100%; } +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; border:1px solid #ddd} +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; } +/* + * jQuery UI Autocomplete 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete#theming + */ +.ui-autocomplete { position: absolute; cursor: default; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.16 + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; + background: #fff !important; + background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#FFF6BF)) !important; + background: -moz-linear-gradient(top,#fff,#FFF6BF) !important; + background: transparent 9 !important; + border-radius:0px; + border-color:white; + border-bottom: 1px solid #E2EAEE; + border-top: 1px solid #E2EAEE; + +} +/* + * jQuery UI Button 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button#theming + */ +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ +/* + * jQuery UI Dialog 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog#theming + */ +.ui-dialog { position: absolute; padding: 0; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; background: #333; color:#eaeaea } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0;} +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; background:#eaeaea} +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } +.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* + * jQuery UI Slider 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* + * jQuery UI Tabs 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; padding: 0; border:none; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap;} +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; width:200px} +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 0; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI Datepicker 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* + * jQuery UI Progressbar 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar#theming + */ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } diff --git a/vendor/plugins/.gitkeep b/vendor/plugins/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From b21e9afed36db08e025c643b47fab3cecbb12b77 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 14 Oct 2011 09:39:56 +0300 Subject: Edited README.rdoc via GitHub --- README.rdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 200977877bc..dc74969324d 100644 --- a/README.rdoc +++ b/README.rdoc @@ -45,7 +45,8 @@ sqlite as default db Install gitosis, edit conf/gitosis.yml & start server - rails s + rails s -e production + == Install Gitosis sudo aptitude install gitosis -- cgit v1.2.1 From 61381c8998fd35c1df3e251ab4e3eddefb2231ea Mon Sep 17 00:00:00 2001 From: William DURAND Date: Fri, 14 Oct 2011 13:48:03 +0300 Subject: The repository "seed-fu" does not have `rails-3-1` branch. Fixes installation using "bundle". --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 353af0e190c..374b50318b9 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "carrierwave" gem 'six' gem 'therubyracer' gem 'faker' -gem 'seed-fu', :branch => 'rails-3-1', :git => 'git://github.com/mbleigh/seed-fu.git' +gem 'seed-fu', :git => 'git://github.com/mbleigh/seed-fu.git' gem "inifile" gem "albino", :git => "git://github.com/gitlabhq/albino.git" gem "kaminari" -- cgit v1.2.1 From 0541b3f3c5dcd291d144c83d9731c75ee811b4e0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 15 Oct 2011 01:11:15 +0300 Subject: 1.0.1 --- CHANGELOG | 5 + Gemfile | 2 +- Gemfile.lock | 9 +- VERSION | 2 +- app/controllers/application_controller.rb | 20 ++++ app/controllers/commits_controller.rb | 15 +-- app/controllers/keys_controller.rb | 8 -- app/controllers/projects_controller.rb | 141 +++++++++++------------- app/controllers/team_members_controller.rb | 5 - app/views/admin/projects/index.html.haml | 2 +- app/views/admin/team_members/index.html.haml | 2 +- app/views/admin/users/index.html.haml | 2 +- app/views/commits/index.html.haml | 4 +- app/views/notes/_notes.html.haml | 1 + app/views/projects/_list.html.haml | 3 - app/views/projects/_projects_top_menu.html.haml | 3 +- app/views/projects/_tree.html.haml | 6 +- app/views/projects/_tree_file.html.haml | 1 - app/views/projects/_tree_item.html.haml | 4 +- configure.rb | 5 - install.rb | 32 ++++++ install/prepare.rb | 51 --------- lib/color.rb | 25 +++++ spec/requests/projects_tree_perfomance_spec.rb | 37 +++++++ update.rb | 26 +++++ 25 files changed, 233 insertions(+), 178 deletions(-) delete mode 100644 configure.rb create mode 100644 install.rb delete mode 100644 install/prepare.rb create mode 100644 lib/color.rb create mode 100644 spec/requests/projects_tree_perfomance_spec.rb create mode 100644 update.rb diff --git a/CHANGELOG b/CHANGELOG index 121ae39855d..1201a85762b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +v 1.0.1 + - fixed: with invalid source code for commit + - fixed: lose branch/tag selection when use tree navigateion + - when history clicked - display path + - bug fix & code cleaning v 1.0.0 - bug fix - projects preview mode diff --git a/Gemfile b/Gemfile index 353af0e190c..374b50318b9 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "carrierwave" gem 'six' gem 'therubyracer' gem 'faker' -gem 'seed-fu', :branch => 'rails-3-1', :git => 'git://github.com/mbleigh/seed-fu.git' +gem 'seed-fu', :git => 'git://github.com/mbleigh/seed-fu.git' gem "inifile" gem "albino", :git => "git://github.com/gitlabhq/albino.git" gem "kaminari" diff --git a/Gemfile.lock b/Gemfile.lock index 46c908a1d62..c02ad3cdc18 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,12 +22,11 @@ GIT GIT remote: git://github.com/mbleigh/seed-fu.git - revision: 29fe8c61ca6cc4408115ea7475fe2647081bd348 - branch: rails-3-1 + revision: 77be06852b18fb01e272ab763ddb292da575586c specs: - seed-fu (2.0.1.rails31) - activerecord (~> 3.1.0.rc4) - activesupport (~> 3.1.0.rc4) + seed-fu (2.1.0) + activerecord (~> 3.1.0) + activesupport (~> 3.1.0) GEM remote: http://rubygems.org/ diff --git a/VERSION b/VERSION index 3eefcb9dd5b..7dea76edb3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 829126fee05..f8eb238b1d2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,4 +41,24 @@ class ApplicationController < ActionController::Base super end end + + def load_refs + @branch = unless params[:branch].blank? + params[:branch] + else + nil + end + + @tag = unless params[:tag].blank? + params[:tag] + else + nil + end + + @ref = @branch || @tag || "master" + end + + def render_404 + render :file => File.join(Rails.root, "public", "404"), :layout => false, :status => "404" + end end diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index d7daec13f0f..af82b1cd901 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -8,25 +8,19 @@ class CommitsController < ApplicationController before_filter :authorize_read_project! def index + load_refs # load @branch, @tag & @ref + @repo = project.repo - @branch = if !params[:branch].blank? - params[:branch] - elsif !params[:tag].blank? - params[:tag] - else - "master" - end if params[:path] - @commits = @repo.log(@branch, params[:path], :max_count => params[:limit] || 100, :skip => params[:offset] || 0) + @commits = @repo.log(@ref, params[:path], :max_count => params[:limit] || 100, :skip => params[:offset] || 0) else - @commits = @repo.commits(@branch, params[:limit] || 100, params[:offset] || 0) + @commits = @repo.commits(@ref, params[:limit] || 100, params[:offset] || 0) end respond_to do |format| format.html # index.html.erb format.js - format.json { render json: @commits } end end @@ -38,7 +32,6 @@ class CommitsController < ApplicationController respond_to do |format| format.html # show.html.erb format.js - format.json { render json: @commit } end end end diff --git a/app/controllers/keys_controller.rb b/app/controllers/keys_controller.rb index 003de6b301a..b0249375b85 100644 --- a/app/controllers/keys_controller.rb +++ b/app/controllers/keys_controller.rb @@ -3,11 +3,6 @@ class KeysController < ApplicationController def index @keys = current_user.keys.all - - respond_to do |format| - format.html # index.html.erb - format.json { render json: @keys } - end end def new @@ -23,8 +18,6 @@ class KeysController < ApplicationController respond_with(@key) end - # DELETE /keys/1 - # DELETE /keys/1.json def destroy @key = current_user.keys.find(params[:id]) @key.destroy @@ -32,7 +25,6 @@ class KeysController < ApplicationController respond_to do |format| format.html { redirect_to keys_url } format.js { render :nothing => true } - format.json { head :ok } end end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 080a6ee62f6..1cc2fd63faa 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -8,78 +8,10 @@ class ProjectsController < ApplicationController def index @projects = current_user.projects.all - - respond_to do |format| - format.html # index.html.erb - format.json { render json: @projects } - end - end - - def show - @repo = project.repo - @commit = @repo.commits.first - @tree = @commit.tree - @tree = @tree / params[:path] if params[:path] - - respond_to do |format| - format.html # show.html.erb - format.json { render json: project } - end - rescue Grit::NoSuchPathError => ex - respond_to do |format| - format.html {render "projects/empty"} - end - end - - def tree - @repo = project.repo - @branch = if !params[:branch].blank? - params[:branch] - elsif !params[:tag].blank? - params[:tag] - else - "master" - end - - if params[:commit_id] - @commit = @repo.commits(params[:commit_id]).first - else - @commit = @repo.commits(@branch || "master").first - end - @tree = @commit.tree - @tree = @tree / params[:path] if params[:path] - - respond_to do |format| - format.html # show.html.erb - format.js do - # temp solution - response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" - response.headers["Pragma"] = "no-cache" - response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" - end - format.json { render json: project } - end - end - - def blob - @repo = project.repo - @commit = project.commit(params[:commit_id]) - @tree = project.tree(@commit, params[:path]) - - if @tree.is_a?(Grit::Blob) - send_data(@tree.data, :type => @tree.mime_type, :disposition => 'inline', :filename => @tree.name) - else - head(404) - end end def new @project = Project.new - - respond_to do |format| - format.html # new.html.erb - format.json { render json: @project } - end end def edit @@ -98,11 +30,9 @@ class ProjectsController < ApplicationController if @project.valid? format.html { redirect_to @project, notice: 'Project was successfully created.' } format.js - format.json { render json: @project, status: :created, location: @project } else format.html { render action: "new" } format.js - format.json { render json: @project.errors, status: :unprocessable_entity } end end rescue Gitosis::AccessDenied @@ -112,7 +42,6 @@ class ProjectsController < ApplicationController respond_to do |format| format.html { render action: "new" } format.js - format.json { render json: @project.errors, status: :unprocessable_entity } end end @@ -121,29 +50,87 @@ class ProjectsController < ApplicationController if project.update_attributes(params[:project]) format.html { redirect_to project, notice: 'Project was successfully updated.' } format.js - format.json { head :ok } else format.html { render action: "edit" } format.js - format.json { render json: project.errors, status: :unprocessable_entity } end end end - def destroy - project.destroy + def show + @repo = project.repo + @commit = @repo.commits.first + @tree = @commit.tree + @tree = @tree / params[:path] if params[:path] + rescue Grit::NoSuchPathError => ex respond_to do |format| - format.html { redirect_to projects_url } - format.json { head :ok } + format.html {render "projects/empty"} end end + # + # Wall + # + def wall @notes = @project.common_notes @note = Note.new end + # + # Repository preview + # + + def tree + load_refs # load @branch, @tag & @ref + + @repo = project.repo + + if params[:commit_id] + @commit = @repo.commits(params[:commit_id]).first + else + @commit = @repo.commits(@ref || "master").first + end + + @tree = @commit.tree + @tree = @tree / params[:path] if params[:path] + + respond_to do |format| + format.html # show.html.erb + format.js do + # diasbale cache to allow back button works + response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" + end + end + rescue + return render_404 + end + + def blob + @repo = project.repo + @commit = project.commit(params[:commit_id]) + @tree = project.tree(@commit, params[:path]) + + if @tree.is_a?(Grit::Blob) + send_data(@tree.data, :type => @tree.mime_type, :disposition => 'inline', :filename => @tree.name) + else + head(404) + end + rescue + return render_404 + end + + def destroy + project.destroy + + respond_to do |format| + format.html { redirect_to projects_url } + end + end + protected def project diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index fd3c944b289..e00cc36c0eb 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -12,7 +12,6 @@ class TeamMembersController < ApplicationController respond_to do |format| format.html # show.html.erb format.js - format.json { render json: @team_member } end end @@ -22,7 +21,6 @@ class TeamMembersController < ApplicationController respond_to do |format| format.html # new.html.erb format.js - format.json { render json: @team_member } end end @@ -34,11 +32,9 @@ class TeamMembersController < ApplicationController if @team_member.save format.html { redirect_to @team_member, notice: 'Team member was successfully created.' } format.js - format.json { render json: @team_member, status: :created, location: @team_member } else format.html { render action: "new" } format.js - format.json { render json: @team_member.errors, status: :unprocessable_entity } end end end @@ -59,7 +55,6 @@ class TeamMembersController < ApplicationController respond_to do |format| format.html { redirect_to root_path } - format.json { head :ok } format.js { render :nothing => true } end end diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 55c41ec31bb..327561dea99 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -11,7 +11,7 @@ - @admin_projects.each do |project| %tr - %td= project.name + %td= link_to project.name, [:admin, project] %td= project.code %td= project.path %td= project.users_projects.count diff --git a/app/views/admin/team_members/index.html.haml b/app/views/admin/team_members/index.html.haml index 4076917da64..bbd7a6c0af8 100644 --- a/app/views/admin/team_members/index.html.haml +++ b/app/views/admin/team_members/index.html.haml @@ -14,7 +14,7 @@ - members.each do |tm| - user = tm.user %tr - %td.span-6= tm.user_name + %td.span-6= link_to tm.user_name, admin_team_member_path(tm) %td.span-6= tm.user_email %td.span-1= check_box_tag "read", 1, project.readers.include?(user), :disabled => :disabled %td.span-1= check_box_tag "commit", 1, project.writers.include?(user), :disabled => :disabled diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index db60a613bce..3b5aa3996bb 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -11,7 +11,7 @@ - @admin_users.each do |user| %tr %td= check_box_tag "admin", 1, user.admin, :disabled => :disabled - %td= user.name + %td= link_to user.name, [:admin, user] %td= user.email %td= user.users_projects.count %td= link_to 'Show', [:admin, user] diff --git a/app/views/commits/index.html.haml b/app/views/commits/index.html.haml index 95579119aee..452e13f8bdb 100644 --- a/app/views/commits/index.html.haml +++ b/app/views/commits/index.html.haml @@ -6,8 +6,10 @@   .left.prepend-1 = form_tag project_commits_path(@project), :method => :get do - = select_tag "tag", options_for_select(@project.tags, @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" + = select_tag "tag", options_for_select(@project.tags, @tag), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" = text_field_tag "ssh", @project.url_to_repo, :class => ["ssh_project_url", "one_click_select"] .clear + - if params[:path] + %h3{:style => "color:#555"} /#{params[:path]} %div{:id => dom_id(@project)} = render "commits" diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml index 457bb8db763..9d1d4b941c8 100644 --- a/app/views/notes/_notes.html.haml +++ b/app/views/notes/_notes.html.haml @@ -1,5 +1,6 @@ %ul#notes-list - @notes.each do |note| + - next unless note.author = render :partial => "notes/show", :locals => {:note => note} %br diff --git a/app/views/projects/_list.html.haml b/app/views/projects/_list.html.haml index 82e1948c757..aec61ffbbe1 100644 --- a/app/views/projects/_list.html.haml +++ b/app/views/projects/_list.html.haml @@ -1,6 +1,3 @@ --#- if current_user.can_create_project? - = link_to 'New Project', new_project_path, :class => "lbutton vm" - %table.round-borders#projects-list %tr %th Name diff --git a/app/views/projects/_projects_top_menu.html.haml b/app/views/projects/_projects_top_menu.html.haml index be04c57e842..f80f28a8bbb 100644 --- a/app/views/projects/_projects_top_menu.html.haml +++ b/app/views/projects/_projects_top_menu.html.haml @@ -1,6 +1,7 @@ %div.top_project_menu %span= link_to 'All', projects_path, :class => current_page?(projects_path) ? "current" : nil - %span= link_to "New Project", new_project_path, :class => current_page?(:controller => "projects", :action => "new") ? "current" : nil + - if current_user.can_create_project? + %span= link_to "New Project", new_project_path, :class => current_page?(:controller => "projects", :action => "new") ? "current" : nil %span.right = link_to_function(image_tag("list_view_icon.jpg"), "switchProjectView()", :style => "border:none;box-shadow:none;") diff --git a/app/views/projects/_tree.html.haml b/app/views/projects/_tree.html.haml index af3a209a8ab..d1903586400 100644 --- a/app/views/projects/_tree.html.haml +++ b/app/views/projects/_tree.html.haml @@ -5,7 +5,7 @@   .left.prepend-1 = form_tag tree_project_path(@project), :method => :get do - = select_tag "tag", options_for_select(@project.tags, @branch), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" + = select_tag "tag", options_for_select(@project.tags, @tag), :onchange => "this.form.submit();", :class => "", :prompt => "Tags" = text_field_tag "ssh", @project.url_to_repo, :class => ["ssh_project_url","one_click_select"] .clear @@ -18,7 +18,7 @@ - if part_path.empty? - part_path = part \/ - = link_to truncate(part, :length => 40), tree_file_project_path(@project, :path => part_path, :commit_id => @commit.try(:id)), :remote => :true + = link_to truncate(part, :length => 40), tree_file_project_path(@project, :path => part_path, :commit_id => @commit.try(:id), :branch => @branch, :tag => @tag), :remote => :true #tree-content-holder - if tree.is_a?(Grit::Blob) = render :partial => "projects/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree } @@ -36,7 +36,7 @@ %tr{ :class => "tree-item", :url => tree_file_project_path(@project, @commit.id, file) } %td.tree-item-file-name = image_tag "dir.png" - = link_to "..", tree_file_project_path(@project, @commit.id, file), :remote => :true + = link_to "..", tree_file_project_path(@project, @commit.id, file, :branch => @branch, :tag => @tag), :remote => :true %td %td diff --git a/app/views/projects/_tree_file.html.haml b/app/views/projects/_tree_file.html.haml index 08927fc2aac..3463bfc543f 100644 --- a/app/views/projects/_tree_file.html.haml +++ b/app/views/projects/_tree_file.html.haml @@ -3,7 +3,6 @@ .view_file_header %strong = name - -#= file.mime_type = link_to "raw", blob_project_path(@project, :commit_id => @commit.id, :path => params[:path] ), :class => "right", :target => "_blank" = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right", :style => "margin-right:10px;" %br/ diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 7179368a0c3..9ba33c1ef88 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -1,5 +1,5 @@ - file = params[:path] ? File.join(params[:path], content.name) : content.name -- content_commit = @project.repo.log(@branch, file, :max_count => 1).last +- content_commit = @project.repo.log(@commit.id, file, :max_count => 1).last - return unless content_commit %tr{ :class => "tree-item", :url => tree_file_project_path(@project, @commit.id, file) } %td.tree-item-file-name @@ -7,7 +7,7 @@ = image_tag "txt.png" - else = image_tag "dir.png" - = link_to truncate(content.name, :length => 40), tree_file_project_path(@project, @commit.id, file), :remote => :true + = link_to truncate(content.name, :length => 40), tree_file_project_path(@project, @commit.id, file, :branch => @branch, :tag => @tag), :remote => :true %td = time_ago_in_words(content_commit.committed_date) ago diff --git a/configure.rb b/configure.rb deleted file mode 100644 index 27bad806ff6..00000000000 --- a/configure.rb +++ /dev/null @@ -1,5 +0,0 @@ -root_path = File.expand_path(File.dirname(__FILE__)) -require File.join(root_path, "install", "prepare") -env = ARGV[0] || "development" - -Install.prepare(env) diff --git a/install.rb b/install.rb new file mode 100644 index 00000000000..a118cb5d6f1 --- /dev/null +++ b/install.rb @@ -0,0 +1,32 @@ +root_path = File.expand_path(File.dirname(__FILE__)) +require File.join(root_path, "lib", "color") +include Color + +# +# ruby ./update.rb development # or test or production (default) +# +envs = ["production", "test", "development"] +env = if envs.include?(ARGV[0]) + ARGV[0] + else + "production" + end + +puts green " == Install for ENV=#{env} ..." + +# bundle install +`bundle install` + +# migrate db +`bundle exec rake db:create RAILS_ENV=#{env}` +`bundle exec rake db:schema:load RAILS_ENV=#{env}` +`bundle exec rake db:seed_fu RAILS_ENV=#{env}` + +puts green %q[ +Administrator account created: + +login.........admin@local.host +password......5iveL!fe +] + +puts green " == Done! Now you can start server" diff --git a/install/prepare.rb b/install/prepare.rb deleted file mode 100644 index f85c01a01ec..00000000000 --- a/install/prepare.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Install - class << self - def prepare(env) - puts green " == Starting for ENV=#{env} ..." - puts "rvm detected" if is_rvm? - - bundler - db(env) - - puts green " == Done! Now you can start server" - end - - def bundler - command 'gem install bundler' - command 'bundle install' - end - - def db(env) - command "bundle exec rake db:setup RAILS_ENV=#{env}" - command "bundle exec rake db:seed_fu RAILS_ENV=#{env}" - end - - def is_rvm? - `type rvm | head -1` =~ /^rvm is/ - end - - def colorize(text, color_code) - "\033[#{color_code}#{text}\033[0m" - end - - def red(text) - colorize(text, "31m") - end - - def green(text) - colorize(text, "32m") - end - - def command(string) - `#{string}` - if $?.to_i > 0 - puts red " == #{string} - FAIL" - puts red " == Error during configure" - exit - else - puts green " == #{string} - OK" - end - end - end -end - diff --git a/lib/color.rb b/lib/color.rb new file mode 100644 index 00000000000..d5500aca8e2 --- /dev/null +++ b/lib/color.rb @@ -0,0 +1,25 @@ +module Color + def colorize(text, color_code) + "\033[#{color_code}#{text}\033[0m" + end + + def red(text) + colorize(text, "31m") + end + + def green(text) + colorize(text, "32m") + end + + def command(string) + `#{string}` + if $?.to_i > 0 + puts red " == #{string} - FAIL" + puts red " == Error during configure" + exit + else + puts green " == #{string} - OK" + end + end +end + diff --git a/spec/requests/projects_tree_perfomance_spec.rb b/spec/requests/projects_tree_perfomance_spec.rb new file mode 100644 index 00000000000..5b4facb17d6 --- /dev/null +++ b/spec/requests/projects_tree_perfomance_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +require 'benchmark' + +describe "Projects" do + before { login_as :user } + + describe "GET /projects/tree" do + describe "head" do + before do + @project = Factory :project + @project.add_access(@user, :read) + + end + + it "should be fast" do + time = Benchmark.realtime do + visit tree_project_path(@project) + end + (time < 1.0).should be_true + end + end + + describe ValidCommit::ID do + before do + @project = Factory :project + @project.add_access(@user, :read) + end + + it "should be fast" do + time = Benchmark.realtime do + visit tree_project_path(@project, :commit_id => ValidCommit::ID) + end + (time < 1.0).should be_true + end + end + end +end diff --git a/update.rb b/update.rb new file mode 100644 index 00000000000..d7eec2898b2 --- /dev/null +++ b/update.rb @@ -0,0 +1,26 @@ +root_path = File.expand_path(File.dirname(__FILE__)) +require File.join(root_path, "lib", "color") +include Color + +# +# ruby ./update.rb development # or test or production (default) +# +envs = ["production", "test", "development"] +env = if envs.include?(ARGV[0]) + ARGV[0] + else + "production" + end + +puts green " == Update for ENV=#{env}" + +# pull from github +`git pull` + +`bundle install` + +# migrate db +`bundle exec rake db:migrate RAILS_ENV=#{env}` + + +puts green " == Done! Now you can start/restart server" -- cgit v1.2.1 From ef2bf1520489a23ff64224c3e102a03b46243dbf Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 15 Oct 2011 21:39:11 +0300 Subject: 1.0.2 --- Gemfile | 1 + Gemfile.lock | 2 ++ app/assets/images/move.png | Bin 0 -> 260 bytes app/assets/javascripts/dashboard.js.coffee | 3 --- app/assets/javascripts/issues.js.coffee | 3 --- app/assets/javascripts/profile.js.coffee | 3 --- app/assets/javascripts/projects.js | 1 + app/assets/stylesheets/projects.css.scss | 10 ++++++++ app/controllers/application_controller.rb | 4 ++++ app/controllers/commits_controller.rb | 1 + app/controllers/issues_controller.rb | 12 +++++++++- app/controllers/projects_controller.rb | 4 +++- app/models/issue.rb | 2 ++ app/models/project.rb | 13 +++++------ app/views/issues/_show.html.haml | 1 + app/views/issues/index.html.haml | 26 +++++++++++++++++++++ app/views/issues/index.js.haml | 1 + app/views/layouts/_head_panel.html.erb | 9 +++---- app/views/notify/note_commit_email.html.haml | 2 +- app/views/notify/note_issue_email.html.haml | 2 +- app/views/notify/note_wall_email.html.haml | 2 +- app/views/projects/index.html.haml | 13 +++++++---- config/initializers/rails_footnotes.rb | 6 ++--- config/routes.rb | 8 +++++-- .../20111015154310_add_position_to_issues.rb | 5 ++++ db/schema.rb | 3 ++- spec/requests/admin/admin_projects_spec.rb | 2 +- spec/requests/projects_spec.rb | 2 +- 28 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 app/assets/images/move.png delete mode 100644 app/assets/javascripts/dashboard.js.coffee delete mode 100644 app/assets/javascripts/issues.js.coffee delete mode 100644 app/assets/javascripts/profile.js.coffee create mode 100644 db/migrate/20111015154310_add_position_to_issues.rb diff --git a/Gemfile b/Gemfile index 374b50318b9..523793e7ca5 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ gem "albino", :git => "git://github.com/gitlabhq/albino.git" gem "kaminari" gem "thin" gem "git" +gem "acts_as_list" group :assets do gem 'sass-rails', " ~> 3.1.0" diff --git a/Gemfile.lock b/Gemfile.lock index c02ad3cdc18..2aa894aa87f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,7 @@ GEM activesupport (= 3.1.0) activesupport (3.1.0) multi_json (~> 1.0) + acts_as_list (0.1.4) addressable (2.2.6) ansi (1.3.0) archive-tar-minitar (0.5.2) @@ -240,6 +241,7 @@ PLATFORMS ruby DEPENDENCIES + acts_as_list albino! annotate! autotest diff --git a/app/assets/images/move.png b/app/assets/images/move.png new file mode 100644 index 00000000000..9d2d55ddf0b Binary files /dev/null and b/app/assets/images/move.png differ diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee deleted file mode 100644 index 761567942fc..00000000000 --- a/app/assets/javascripts/dashboard.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee deleted file mode 100644 index 761567942fc..00000000000 --- a/app/assets/javascripts/issues.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee deleted file mode 100644 index 761567942fc..00000000000 --- a/app/assets/javascripts/profile.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/projects.js b/app/assets/javascripts/projects.js index dc13d006077..74f0e643c21 100644 --- a/app/assets/javascripts/projects.js +++ b/app/assets/javascripts/projects.js @@ -34,6 +34,7 @@ $(document).ready(function(){ e.preventDefault(); } }); + }); function focusSearch() { diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 0ee40f512ac..bc15d8e2c12 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -539,3 +539,13 @@ tbody tr:nth-child(2n) td, tbody tr.even td { float:left; } } + +.handle:hover{ + cursor: move; +} + +.handle{ + width: 12px; + height: 12px; + padding: 10px; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f8eb238b1d2..2866d1a7358 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -61,4 +61,8 @@ class ApplicationController < ActionController::Base def render_404 render :file => File.join(Rails.root, "public", "404"), :layout => false, :status => "404" end + + def require_non_empty_project + redirect_to @project unless @project.repo_exists? + end end diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index af82b1cd901..83071891be4 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -6,6 +6,7 @@ class CommitsController < ApplicationController # Authorize before_filter :add_project_abilities before_filter :authorize_read_project! + before_filter :require_non_empty_project def index load_refs # load @branch, @tag & @ref diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index f8b4719809b..6da5dea8bf7 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -5,7 +5,7 @@ class IssuesController < ApplicationController # Authorize before_filter :add_project_abilities before_filter :authorize_read_issue! - before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update] + before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update, :sort] before_filter :authorize_admin_issue!, :only => [:destroy] respond_to :js @@ -69,4 +69,14 @@ class IssuesController < ApplicationController format.js { render :nothing => true } end end + + def sort + @issues = @project.issues.where(:id => params['issue']) + @issues.each do |issue| + issue.position = params['issue'].index(issue.id.to_s) + 1 + issue.save + end + + render :nothing => true + end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 1cc2fd63faa..e8f1c3925c9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -6,6 +6,8 @@ class ProjectsController < ApplicationController before_filter :authorize_read_project!, :except => [:index, :new, :create] before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy] + before_filter :require_non_empty_project, :only => [:blob, :tree] + def index @projects = current_user.projects.all end @@ -48,7 +50,7 @@ class ProjectsController < ApplicationController def update respond_to do |format| if project.update_attributes(params[:project]) - format.html { redirect_to project, notice: 'Project was successfully updated.' } + format.html { redirect_to project, :notice => 'Project was successfully updated.' } format.js else format.html { render action: "edit" } diff --git a/app/models/issue.rb b/app/models/issue.rb index 0399607efe6..9b1b923eaa0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base scope :opened, where(:closed => false) scope :closed, where(:closed => true) scope :assigned, lambda { |u| where(:assignee_id => u.id)} + + acts_as_list end # == Schema Information # diff --git a/app/models/project.rb b/app/models/project.rb index 17b556c3471..3c07976d24e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3,7 +3,7 @@ require "grit" class Project < ActiveRecord::Base belongs_to :owner, :class_name => "User" - has_many :issues, :dependent => :destroy + has_many :issues, :dependent => :destroy, :order => "position" has_many :users_projects, :dependent => :destroy has_many :users, :through => :users_projects has_many :notes, :dependent => :destroy @@ -16,6 +16,8 @@ class Project < ActiveRecord::Base validates :path, :uniqueness => true, :presence => true, + :format => { :with => /^[a-zA-Z0-9_\-]*$/, + :message => "only letters, digits & '_' '-' allowed" }, :length => { :within => 0..255 } validates :description, @@ -24,14 +26,15 @@ class Project < ActiveRecord::Base validates :code, :presence => true, :uniqueness => true, - :length => { :within => 3..12 } + :format => { :with => /^[a-zA-Z0-9_\-]*$/, + :message => "only letters, digits & '_' '-' allowed" }, + :length => { :within => 3..16 } validates :owner, :presence => true validate :check_limit - before_save :format_code after_destroy :destroy_gitosis_project after_save :update_gitosis_project @@ -47,10 +50,6 @@ class Project < ActiveRecord::Base notes.where(:noteable_type => ["", nil]) end - def format_code - read_attribute(:code).downcase.strip.gsub(' ', '') - end - def update_gitosis_project Gitosis.new.configure do |c| c.update_project(path, gitosis_writers) diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml index 131d0d4c8a2..30157257ff5 100644 --- a/app/views/issues/_show.html.haml +++ b/app/views/issues/_show.html.haml @@ -1,5 +1,6 @@ %tr{ :id => dom_id(issue), :class => "issue", :url => project_issue_path(@project, issue) } %td + = image_tag "move.png" , :class => [:handle, :left] = image_tag gravatar_icon(issue.assignee.email), :class => "left", :width => 40, :style => "padding:0 5px;" = truncate issue.assignee.name, :lenght => 20 %td ##{issue.id} diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index 7157d2c385d..6f4548a8066 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -22,3 +22,29 @@ :javascript $('.delete-issue').live('ajax:success', function() { $(this).closest('tr').fadeOut(); }); + + function setSortable(){ + $('#issues-table>tbody').sortable({ + axis: 'y', + dropOnEmpty: false, + handle: '.handle', + cursor: 'crosshair', + items: 'tr', + opacity: 0.4, + scroll: true, + update: function(){ + $.ajax({ + type: 'post', + data: $('#issues-table>tbody').sortable('serialize'), + dataType: 'script', + complete: function(request){ + $('#issues-table>tbody').effect('highlight'); + }, + url: "#{sort_project_issues_path(@project)}"}) + } + }); + } + + $(function(){ + setSortable(); + }); diff --git a/app/views/issues/index.js.haml b/app/views/issues/index.js.haml index 1f051309233..bc18ac15151 100644 --- a/app/views/issues/index.js.haml +++ b/app/views/issues/index.js.haml @@ -1,2 +1,3 @@ :plain $('#issues-table-holder').html("#{escape_javascript(render('issues'))}"); + setSortable(); diff --git a/app/views/layouts/_head_panel.html.erb b/app/views/layouts/_head_panel.html.erb index f32e4bc2b71..047f708b6df 100644 --- a/app/views/layouts/_head_panel.html.erb +++ b/app/views/layouts/_head_panel.html.erb @@ -10,12 +10,13 @@
    <%= link_to truncate(@project.name, :length => 20), project_path(@project), :class => "current button" if @project && !@project.new_record? %> - <%= link_to 'Home', root_path, :class => current_page?(root_url) ? "current button" : "button" %> <%= link_to 'Projects', projects_path, :class => current_page?(projects_path) ? "current button" : "button" %> - <%= link_to 'Profile', profile_path, :class => (controller.controller_name == "keys") ? "current button" : "button" %> <%= link_to('Admin', admin_root_path, :class => admin_namespace? ? "current button" : "button" ) if current_user.is_admin? %> - <%#= link_to 'Profile', edit_user_registration_path, :class => "button" %> - <%= link_to 'Logout', destroy_user_session_path, :class => "button", :method => :delete %> + <%= link_to profile_path, :class => ((controller.controller_name == "keys" || controller.controller_name == "profile") ? "current button" : "button") do %> + <%= image_tag gravatar_icon(current_user.email) %> + <%= current_user.name.split(" ").first %> + <% end %> + <%= link_to 'Logout', destroy_user_session_path, :style => "border-left: 1px solid #666;", :class => "button", :method => :delete %>
    diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml index 09ae54ac629..81001d684c2 100644 --- a/app/views/notify/note_commit_email.html.haml +++ b/app/views/notify/note_commit_email.html.haml @@ -5,7 +5,7 @@ %td{:align => "left", :style => "padding: 20px 0 0;"} %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} New comment for commmit - = link_to truncate(@commit.id.to_s, :length => 16), project_commit_url(@project, :id => @commit.id) + = link_to truncate(@commit.id.to_s, :length => 16), project_commit_url(@project, :id => @commit.id, :anchor => "note_#{@note.id}") %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} %tr %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} diff --git a/app/views/notify/note_issue_email.html.haml b/app/views/notify/note_issue_email.html.haml index 54982af6286..242c97bf25f 100644 --- a/app/views/notify/note_issue_email.html.haml +++ b/app/views/notify/note_issue_email.html.haml @@ -5,7 +5,7 @@ %td{:align => "left", :style => "padding: 20px 0 0;"} %h2{:style => "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} New comment - - = link_to project_issue_url(@project, @issue) do + = link_to project_issue_url(@project, @issue, :anchor => "note_#{@note.id}") do = "Issue ##{@issue.id.to_s}" = truncate(@issue.title, :length => 35) %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} diff --git a/app/views/notify/note_wall_email.html.haml b/app/views/notify/note_wall_email.html.haml index 285fc763613..c9fdae12677 100644 --- a/app/views/notify/note_wall_email.html.haml +++ b/app/views/notify/note_wall_email.html.haml @@ -5,7 +5,7 @@ %td{:align => "left", :style => "padding: 20px 0 0;"} %h2{:style => "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} New message on - = link_to "Project Wall", wall_project_url(@project) + = link_to "Project Wall", wall_project_url(@project, :anchor => "note_#{@note.id}") %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} %tr %td{:style => "font-size: 1px; line-height: 1px;", :width => "21"} diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 22af6b7551a..888a72b6811 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -1,4 +1,9 @@ -%div{:class => "tile", :style => view_mode_style("tile")} - = render "tile" -%div{:class => "list", :style => view_mode_style("list")} - = render "list" +- unless @projects.empty? + %div{:class => "tile", :style => view_mode_style("tile")} + = render "tile" + %div{:class => "list", :style => view_mode_style("list")} + = render "list" +- else + %center.prepend-top + %h2 + %cite Nothing here diff --git a/config/initializers/rails_footnotes.rb b/config/initializers/rails_footnotes.rb index da9d58e4acb..db71e39c608 100644 --- a/config/initializers/rails_footnotes.rb +++ b/config/initializers/rails_footnotes.rb @@ -1,5 +1,5 @@ -if defined?(Footnotes) && Rails.env.development? - Footnotes.run! # first of all +#if defined?(Footnotes) && Rails.env.development? + #Footnotes.run! # first of all # ... other init code -end +#end diff --git a/config/routes.rb b/config/routes.rb index 00106b119a6..acf92536e3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,7 +32,7 @@ Gitlab::Application.routes.draw do get "tree/:commit_id/:path" => "projects#tree", :as => :tree_file, :constraints => { - :id => /[a-zA-Z0-9]+/, + :id => /[a-zA-Z0-9_\-]+/, :commit_id => /[a-zA-Z0-9]+/, :path => /.*/ } @@ -40,7 +40,11 @@ Gitlab::Application.routes.draw do end resources :commits resources :team_members - resources :issues + resources :issues do + collection do + post :sort + end + end resources :notes, :only => [:create, :destroy] end root :to => "projects#index" diff --git a/db/migrate/20111015154310_add_position_to_issues.rb b/db/migrate/20111015154310_add_position_to_issues.rb new file mode 100644 index 00000000000..41451a0cabb --- /dev/null +++ b/db/migrate/20111015154310_add_position_to_issues.rb @@ -0,0 +1,5 @@ +class AddPositionToIssues < ActiveRecord::Migration + def change + add_column :issues, :position, :integer, :default => 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index befe0b2a518..ed37dbbb923 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111009111204) do +ActiveRecord::Schema.define(:version => 20111015154310) do create_table "issues", :force => true do |t| t.string "title" @@ -22,6 +22,7 @@ ActiveRecord::Schema.define(:version => 20111009111204) do t.datetime "created_at" t.datetime "updated_at" t.boolean "closed", :default => false, :null => false + t.integer "position", :default => 0 end create_table "keys", :force => true do |t| diff --git a/spec/requests/admin/admin_projects_spec.rb b/spec/requests/admin/admin_projects_spec.rb index 8aa311e8442..e36ee4411ff 100644 --- a/spec/requests/admin/admin_projects_spec.rb +++ b/spec/requests/admin/admin_projects_spec.rb @@ -88,7 +88,7 @@ describe "Admin::Projects" do visit new_admin_project_path fill_in 'Name', :with => 'NewProject' fill_in 'Code', :with => 'NPR' - fill_in 'Path', :with => '/tmp/legit_test/legit' + fill_in 'Path', :with => 'legit_1' expect { click_button "Save" }.to change { Project.count }.by(1) @project = Project.last end diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb index 329f0a50435..2825a6a979a 100644 --- a/spec/requests/projects_spec.rb +++ b/spec/requests/projects_spec.rb @@ -39,7 +39,7 @@ describe "Projects" do visit new_project_path fill_in 'Name', :with => 'NewProject' fill_in 'Code', :with => 'NPR' - fill_in 'Path', :with => '/tmp/legit_test/legit' + fill_in 'Path', :with => 'newproject' expect { click_button "Create Project" }.to change { Project.count }.by(1) @project = Project.last end -- cgit v1.2.1 From 4d37329c732b394dd521a6568be6b7a5b4c3bd68 Mon Sep 17 00:00:00 2001 From: Kevin Lynch Date: Mon, 17 Oct 2011 01:53:30 -0400 Subject: Must ensure that the lock is always removed. --- lib/gitosis.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/gitosis.rb b/lib/gitosis.rb index a9eef96cd3f..4cc5e6e3d08 100644 --- a/lib/gitosis.rb +++ b/lib/gitosis.rb @@ -27,13 +27,16 @@ class Gitosis def configure status = Timeout::timeout(20) do File.open(File.join(Dir.tmpdir,"gitlabhq-gitosis.lock"), "w+") do |f| - f.flock(File::LOCK_EX) + begin + f.flock(File::LOCK_EX) - pull - yield(self) - push + pull + yield(self) + push - f.flock(File::LOCK_UN) + ensure + f.flock(File::LOCK_UN) + end end end rescue Exception => ex -- cgit v1.2.1 From a39d0431cb2dec082cd5353f3c212833e49b3490 Mon Sep 17 00:00:00 2001 From: Kevin Lynch Date: Mon, 17 Oct 2011 18:35:25 -0400 Subject: The project name should not be limited to 16 characters. 16 is too short --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 3c07976d24e..2340940ba5f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,7 +28,7 @@ class Project < ActiveRecord::Base :uniqueness => true, :format => { :with => /^[a-zA-Z0-9_\-]*$/, :message => "only letters, digits & '_' '-' allowed" }, - :length => { :within => 3..16 } + :length => { :within => 3..255 } validates :owner, :presence => true -- cgit v1.2.1 From c374eb80ff9fb71d37faffc15714bf98b632d3e5 Mon Sep 17 00:00:00 2001 From: Vyacheslav Slinko Date: Tue, 18 Oct 2011 15:13:57 +0400 Subject: Increase max key length. Some keys has comment after key string. --- app/models/key.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/key.rb b/app/models/key.rb index 9fa8958772d..29dcbbce0ac 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -8,7 +8,7 @@ class Key < ActiveRecord::Base validates :key, :presence => true, :uniqueness => true, - :length => { :within => 0..555 } + :length => { :within => 0..1024 } before_save :set_identifier after_save :update_gitosis -- cgit v1.2.1 From 6b030fd41d697e327d2935b406cba70b6a460504 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 21 Oct 2011 20:04:41 +0300 Subject: 1.1pre1 --- CHANGELOG | 19 ++++ Gemfile | 2 +- Gemfile.lock | 15 ++- VERSION | 2 +- app/assets/images/home.png | Bin 0 -> 271 bytes app/assets/javascripts/application.js | 4 + app/assets/javascripts/snippets.js | 9 ++ app/assets/stylesheets/highlight.css.scss | 6 +- app/assets/stylesheets/projects.css.scss | 95 ++++++++++++++++++- app/assets/stylesheets/snippets.css.scss | 3 + app/controllers/application_controller.rb | 8 +- app/controllers/issues_controller.rb | 14 ++- app/controllers/notes_controller.rb | 6 +- app/controllers/profile_controller.rb | 6 ++ app/controllers/projects_controller.rb | 32 +++++-- app/controllers/snippets_controller.rb | 62 +++++++++++++ app/controllers/team_members_controller.rb | 4 +- app/helpers/application_helper.rb | 21 ----- app/helpers/commits_helper.rb | 9 ++ app/helpers/projects_helper.rb | 12 +++ app/helpers/snippets_helper.rb | 2 + app/models/ability.rb | 23 +++++ app/models/issue.rb | 1 + app/models/note.rb | 4 + app/models/project.rb | 29 ++++++ app/models/snippet.rb | 51 +++++++++++ app/models/user.rb | 6 +- app/views/admin/users/_form.html.haml | 17 +++- app/views/admin/users/show.html.haml | 11 +++ app/views/commits/_commits.html.haml | 6 +- app/views/commits/_diff.html.haml | 7 +- app/views/commits/show.html.haml | 2 +- app/views/commits/show.js.haml | 4 +- app/views/issues/show.js.haml | 2 + app/views/notes/_form.html.haml | 6 +- app/views/notes/_notes.html.haml | 29 ++++-- app/views/notes/_notes_list.html.haml | 4 + app/views/notes/_show.html.haml | 12 +-- app/views/notes/create.js.haml | 5 +- app/views/profile/show.html.haml | 25 +++++ app/views/projects/_form.html.haml | 6 +- app/views/projects/_recent_commits.html.haml | 18 ++++ app/views/projects/_recent_messages.html.haml | 52 +++++++++++ app/views/projects/_tile.html.haml | 2 +- app/views/projects/_top_menu.html.haml | 10 +- app/views/projects/_tree_file.html.haml | 11 +-- app/views/projects/_tree_item.html.haml | 2 +- app/views/projects/empty.html.erb | 7 +- app/views/projects/show.html.haml | 24 ++++- app/views/projects/wall.html.haml | 28 ++++++ app/views/projects/wall.js.haml | 2 + app/views/snippets/_form.html.haml | 22 +++++ app/views/snippets/_snippet.html.haml | 11 +++ app/views/snippets/edit.html.haml | 1 + app/views/snippets/index.html.haml | 14 +++ app/views/snippets/new.html.haml | 1 + app/views/snippets/show.html.haml | 22 +++++ app/views/team_members/_show.html.haml | 6 +- app/views/team_members/show.html.haml | 28 ++++++ config/initializers/grit_ext.rb | 8 ++ config/routes.rb | 3 + db/migrate/20111016183422_create_snippets.rb | 12 +++ .../20111016193417_add_content_type_to_snippets.rb | 5 + .../20111016195506_add_file_name_to_snippets.rb | 6 ++ db/migrate/20111019212429_add_social_to_user.rb | 7 ++ ...20111021101550_change_social_fields_in_users.rb | 14 +++ db/schema.rb | 15 ++- lib/color.rb | 4 + lib/gitosis.rb | 4 +- lib/utils.rb | 51 ++++++++++- spec/factories.rb | 6 ++ spec/models/issue_spec.rb | 1 + spec/models/snippet_spec.rb | 30 ++++++ spec/models/user_spec.rb | 3 + spec/requests/profile_spec.rb | 16 ++++ spec/requests/projects_security_spec.rb | 27 ++++-- spec/requests/projects_spec.rb | 7 +- spec/requests/snippets_spec.rb | 101 +++++++++++++++++++++ spec/requests/team_members_spec.rb | 9 ++ spec/requests/user_security_spec.rb | 8 +- spec/support/login.rb | 3 +- spec/support/matchers.rb | 17 +++- update.rb | 28 +++++- 83 files changed, 1090 insertions(+), 137 deletions(-) create mode 100644 app/assets/images/home.png create mode 100644 app/assets/javascripts/snippets.js create mode 100644 app/assets/stylesheets/snippets.css.scss create mode 100644 app/controllers/snippets_controller.rb create mode 100644 app/helpers/snippets_helper.rb create mode 100644 app/models/snippet.rb create mode 100644 app/views/issues/show.js.haml create mode 100644 app/views/notes/_notes_list.html.haml create mode 100644 app/views/projects/_recent_commits.html.haml create mode 100644 app/views/projects/_recent_messages.html.haml create mode 100644 app/views/projects/wall.js.haml create mode 100644 app/views/snippets/_form.html.haml create mode 100644 app/views/snippets/_snippet.html.haml create mode 100644 app/views/snippets/edit.html.haml create mode 100644 app/views/snippets/index.html.haml create mode 100644 app/views/snippets/new.html.haml create mode 100644 app/views/snippets/show.html.haml create mode 100644 app/views/team_members/show.html.haml create mode 100644 config/initializers/grit_ext.rb create mode 100644 db/migrate/20111016183422_create_snippets.rb create mode 100644 db/migrate/20111016193417_add_content_type_to_snippets.rb create mode 100644 db/migrate/20111016195506_add_file_name_to_snippets.rb create mode 100644 db/migrate/20111019212429_add_social_to_user.rb create mode 100644 db/migrate/20111021101550_change_social_fields_in_users.rb create mode 100644 spec/models/snippet_spec.rb create mode 100644 spec/requests/snippets_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 1201a85762b..96acfba1440 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,32 @@ +v 1.1.0 + - project dashboard + - wall redesigned + - feature: code snippets + - fixed horizontal scroll on file preview + - fixed app crash if commit message has invalid chars + - bugfix & code cleaning + +v 1.0.2 + - fixed bug with empty project + - added adv validation for project path & code + - feature: issues can be sortable + - bugfix + - username dispalyed on top panel + v 1.0.1 - fixed: with invalid source code for commit - fixed: lose branch/tag selection when use tree navigateion - when history clicked - display path - bug fix & code cleaning + v 1.0.0 - bug fix - projects preview mode + v 0.9.6 - css fix - new repo empty tree until restart server - fixed + v 0.9.4 - security improved - authorization improved @@ -24,6 +42,7 @@ v 0.9.1 - updated app name - issue redesigned - issue can be edit + v 0.8.0 - sytax highlight for main file types - redesign diff --git a/Gemfile b/Gemfile index 523793e7ca5..77a00659ac7 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem 'therubyracer' gem 'faker' gem 'seed-fu', :git => 'git://github.com/mbleigh/seed-fu.git' gem "inifile" -gem "albino", :git => "git://github.com/gitlabhq/albino.git" +gem "pygments.rb", "0.2.3" gem "kaminari" gem "thin" gem "git" diff --git a/Gemfile.lock b/Gemfile.lock index 2aa894aa87f..5ba34459752 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,13 +4,6 @@ GIT specs: annotate (2.4.1.beta1) -GIT - remote: git://github.com/gitlabhq/albino.git - revision: 118380924969f3a856659f86ea1f40c1ba7bfcb1 - specs: - albino (1.3.3) - posix-spawn (>= 0.3.6) - GIT remote: git://github.com/gitlabhq/grit.git revision: ff015074ef35bd94cba943f9c0f98e161ab5851c @@ -72,6 +65,7 @@ GEM ZenTest (= 4.5) awesome_print (0.4.0) bcrypt-ruby (3.0.1) + blankslate (2.1.2.4) builder (3.0.0) capybara (1.0.1) mime-types (>= 1.16) @@ -138,6 +132,8 @@ GEM orm_adapter (0.0.5) polyglot (0.3.2) posix-spawn (0.3.6) + pygments.rb (0.2.3) + rubypython (>= 0.5.1) rack (1.3.2) rack-cache (1.0.3) rack (>= 0.4) @@ -189,6 +185,9 @@ GEM ruby-debug-base19 (>= 0.11.19) ruby_core_source (0.1.5) archive-tar-minitar (>= 0.5.2) + rubypython (0.5.1) + blankslate (>= 2.1.2.3) + ffi (~> 1.0.7) rubyzip (0.9.4) sass (3.1.7) sass-rails (3.1.1) @@ -242,7 +241,6 @@ PLATFORMS DEPENDENCIES acts_as_list - albino! annotate! autotest autotest-rails @@ -260,6 +258,7 @@ DEPENDENCIES jquery-rails kaminari launchy + pygments.rb (= 0.2.3) rails (= 3.1.0) rails-footnotes (>= 3.7.5.rc4) rspec-rails diff --git a/VERSION b/VERSION index 7dea76edb3d..9084fa2f716 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1 +1.1.0 diff --git a/app/assets/images/home.png b/app/assets/images/home.png new file mode 100644 index 00000000000..316d5d4287a Binary files /dev/null and b/app/assets/images/home.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 01e3b416526..024dfe1182a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -16,3 +16,7 @@ $(function(){ $('select#branch').selectmenu({style:'popup', width:200}); $('select#tag').selectmenu({style:'popup', width:200}); }); + +function updatePage(){ + $.ajax({type: "GET", url: location.href, dataType: "script"}); +} diff --git a/app/assets/javascripts/snippets.js b/app/assets/javascripts/snippets.js new file mode 100644 index 00000000000..11e18eb779e --- /dev/null +++ b/app/assets/javascripts/snippets.js @@ -0,0 +1,9 @@ +$(document).ready(function(){ + $("#snippets-table .snippet").live('click', function(e){ + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); +}); diff --git a/app/assets/stylesheets/highlight.css.scss b/app/assets/stylesheets/highlight.css.scss index 05cb98e73cb..31f9369a45f 100644 --- a/app/assets/stylesheets/highlight.css.scss +++ b/app/assets/stylesheets/highlight.css.scss @@ -22,8 +22,8 @@ td.linenos{ .highlight{ background:none; - padding:10px 0px 0px 0; - margin-left:10px; + padding:10px 0px 0px 10px; + margin-left:0px; } .highlight pre{ } @@ -43,7 +43,7 @@ td.linenos { } td.code .highlight { - overflow-x: scroll; + overflow: auto; } table.highlighttable pre{ padding:0; diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index bc15d8e2c12..517886722c1 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -310,6 +310,7 @@ input.ssh_project_url { } #projects-list .project, +#snippets-table .snippet, #issues-table .issue{ cursor:pointer; @@ -360,6 +361,8 @@ input.ssh_project_url { .user_new, .edit_user, .new_project, +.new_snippet, +.edit_snippet, .edit_project { input[type='text'], input[type='email'], @@ -488,8 +491,14 @@ tbody tr:nth-child(2n) td, tbody tr.even td { background: white; } p { - margin-bottom: 3px; - font-size: 12px; + margin-bottom: 4px; + font-size: 13px; + color:#111; + } + } + cite { + &.ago { + color:#666; } } } @@ -512,7 +521,7 @@ tbody tr:nth-child(2n) td, tbody tr.even td { } .note_content { float:left; - width:750px; + width:650px; } .issue_notes { @@ -549,3 +558,83 @@ tbody tr:nth-child(2n) td, tbody tr.even td { height: 12px; padding: 10px; } + +.recent_message_parent { + img { + padding-right:10px; + } + + float: left; + margin: 0 20px 20px 0px; + padding: 5px 0px;; + width: 420px; + + &.dash_wall{ + border-bottom: 2px solid orange; + span { + background: orange; + color:black; + } + } + + &.dash_issue{ + border-bottom: 2px solid #ffbbbb; + span { + background: #ffbbbb; + } + } + &.dash_commit{ + border-bottom: 2px solid #bbbbff; + + span{ + background: #bbbbff; + } + } + + &.dash_snippet{ + border-bottom: 2px solid #bbffbb; + + span{ + background: #bbffbb; + } + } + + + span{ + border: 1px solid #aaa; + color:black; + padding:1px 4px; + } + + h4 { + margin-bottom:3px; + } + +} +.commit, +.message, +#notes-list{ + .author { + background: #eaeaea; + color: #333; + border: 1px solid #aaa; + padding:1px 2px; + margin-right:5px; + } +} + +/* Note textare */ +#note_note { + height:100px; + width:97%; + font-size:14px; +} + +.wall_page { + #note_note { + height:25px; + } + .attach_holder { + display:none; + } +} diff --git a/app/assets/stylesheets/snippets.css.scss b/app/assets/stylesheets/snippets.css.scss new file mode 100644 index 00000000000..1b680d87bd3 --- /dev/null +++ b/app/assets/stylesheets/snippets.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Snippets controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2866d1a7358..047c6cb7d95 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -27,11 +27,15 @@ class ApplicationController < ActionController::Base end def authenticate_admin! - return redirect_to(new_user_session_path) unless current_user.is_admin? + return render_404 unless current_user.is_admin? end def authorize_project!(action) - return redirect_to(new_user_session_path) unless can?(current_user, action, project) + return render_404 unless can?(current_user, action, project) + end + + def access_denied! + render_404 end def method_missing(method_sym, *arguments, &block) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 6da5dea8bf7..cf8e1ebe661 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,12 +1,12 @@ class IssuesController < ApplicationController before_filter :authenticate_user! before_filter :project + before_filter :issue, :only => [:edit, :update, :destroy, :show] # Authorize before_filter :add_project_abilities before_filter :authorize_read_issue! before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update, :sort] - before_filter :authorize_admin_issue!, :only => [:destroy] respond_to :js @@ -30,12 +30,10 @@ class IssuesController < ApplicationController end def edit - @issue = @project.issues.find(params[:id]) respond_with(@issue) end def show - @issue = @project.issues.find(params[:id]) @notes = @issue.notes @note = @project.notes.new(:noteable => @issue) end @@ -51,7 +49,6 @@ class IssuesController < ApplicationController end def update - @issue = @project.issues.find(params[:id]) @issue.update_attributes(params[:issue]) respond_to do |format| @@ -62,7 +59,8 @@ class IssuesController < ApplicationController def destroy - @issue = @project.issues.find(params[:id]) + return access_denied! unless can?(current_user, :admin_issue, @issue) + @issue.destroy respond_to do |format| @@ -79,4 +77,10 @@ class IssuesController < ApplicationController render :nothing => true end + + protected + + def issue + @issue ||= @project.issues.find(params[:id]) + end end diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index d0a40eb18e4..46425664d6e 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -4,7 +4,6 @@ class NotesController < ApplicationController # Authorize before_filter :add_project_abilities before_filter :authorize_write_note!, :only => [:create] - before_filter :authorize_admin_note!, :only => [:destroy] respond_to :js @@ -25,6 +24,9 @@ class NotesController < ApplicationController def destroy @note = @project.notes.find(params[:id]) + + return access_denied! unless can?(current_user, :admin_note, @note) + @note.destroy respond_to do |format| @@ -41,6 +43,8 @@ class NotesController < ApplicationController Notify.note_commit_email(u, @note).deliver when "Issue" then Notify.note_issue_email(u, @note).deliver + when "Snippet" + true else Notify.note_wall_email(u, @note).deliver end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb index 666c6309dce..c8477729a92 100644 --- a/app/controllers/profile_controller.rb +++ b/app/controllers/profile_controller.rb @@ -3,6 +3,12 @@ class ProfileController < ApplicationController @user = current_user end + def social_update + @user = current_user + @user.update_attributes(params[:user]) + redirect_to [:profile] + end + def password @user = current_user end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e8f1c3925c9..54d19af7511 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -60,14 +60,21 @@ class ProjectsController < ApplicationController end def show - @repo = project.repo - @commit = @repo.commits.first - @tree = @commit.tree - @tree = @tree / params[:path] if params[:path] - - rescue Grit::NoSuchPathError => ex - respond_to do |format| - format.html {render "projects/empty"} + return render "projects/empty" unless @project.repo_exists? + @date = case params[:view] + when "week" then Date.today - 7.days + when "day" then Date.today + else nil + end + + if @date + @date = @date.at_beginning_of_day + + @commits = @project.commits_since(@date) + @messages = project.notes.since(@date).order("created_at DESC") + else + @commits = @project.fresh_commits + @messages = project.notes.fresh.limit(10) end end @@ -76,8 +83,15 @@ class ProjectsController < ApplicationController # def wall - @notes = @project.common_notes @note = Note.new + @notes = @project.common_notes.order("created_at DESC") + + @notes = case params[:view] + when "week" then @notes.since((Date.today - 7.days).at_beginning_of_day) + when "all" then @notes.all + when "day" then @notes.since(Date.today.at_beginning_of_day) + else @notes.fresh.limit(10) + end end # diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb new file mode 100644 index 00000000000..b31fe6836a2 --- /dev/null +++ b/app/controllers/snippets_controller.rb @@ -0,0 +1,62 @@ +class SnippetsController < ApplicationController + before_filter :authenticate_user! + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_snippet! + before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort] + + respond_to :html + + def index + @snippets = @project.snippets + end + + def new + @snippet = @project.snippets.new + end + + def create + @snippet = @project.snippets.new(params[:snippet]) + @snippet.author = current_user + @snippet.save + + if @snippet.valid? + redirect_to [@project, @snippet] + else + respond_with(@snippet) + end + end + + def edit + @snippet = @project.snippets.find(params[:id]) + end + + def update + @snippet = @project.snippets.find(params[:id]) + @snippet.update_attributes(params[:snippet]) + + if @snippet.valid? + redirect_to [@project, @snippet] + else + respond_with(@snippet) + end + end + + def show + @snippet = @project.snippets.find(params[:id]) + @notes = @snippet.notes + @note = @project.notes.new(:noteable => @snippet) + end + + def destroy + @snippet = @project.snippets.find(params[:id]) + + return access_denied! unless can?(current_user, :admin_snippet, @snippet) + + @snippet.destroy + + redirect_to project_snippets_path(@project) + end +end diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index e00cc36c0eb..5fb2710dad7 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -3,8 +3,8 @@ class TeamMembersController < ApplicationController # Authorize before_filter :add_project_abilities - before_filter :authorize_read_team_member! - before_filter :authorize_admin_team_member!, :only => [:new, :create, :destroy, :update] + before_filter :authorize_read_project! + before_filter :authorize_admin_project!, :only => [:new, :create, :destroy, :update] def show @team_member = project.users_projects.find(params[:id]) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 89a906f0472..0895eb0d1ba 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -53,25 +53,4 @@ module ApplicationHelper [projects, default_nav, project_nav].flatten.to_json end - def handle_file_type(file_name, mime_type) - if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ - :ruby - elsif file_name =~ /\.py$/ - :python - elsif file_name =~ /(\.pl|\.scala|\.c|\.cpp|\.java|\.haml|\.html|\.sass|\.scss|\.xml|\.php|\.erb)$/ - $1[1..-1].to_sym - elsif file_name =~ /\.js$/ - :javascript - elsif file_name =~ /\.sh$/ - :bash - elsif file_name =~ /\.coffee$/ - :coffeescript - elsif file_name =~ /\.yml$/ - :yaml - elsif file_name =~ /\.md$/ - :minid - else - :text - end - end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f1b546684f0..b79e5718194 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -21,4 +21,13 @@ module CommitsHelper link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit), :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link" end + + # Cause some errors with trucate & encoding use this method + def truncate_commit_message(commit, size = 60) + message = commit.message + message.length > size ? (message[0..(size - 1)] + "...") : message + # if special characters occurs + rescue + commit.message + end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d570dff4639..4a6923afd09 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -3,4 +3,16 @@ module ProjectsHelper cookies["project_view"] ||= "tile" cookies["project_view"] == type ? nil : "display:none" end + + def load_note_parent(id, type, project) + case type + when "Issue" then @project.issues.find(id) + when "Commit" then @project.repo.commits(id).first + when "Snippet" then @project.snippets.find(id) + else + true + end + rescue + nil + end end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb new file mode 100644 index 00000000000..236b6c8c23a --- /dev/null +++ b/app/helpers/snippets_helper.rb @@ -0,0 +1,2 @@ +module SnippetsHelper +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 0a2c45f1289..b822f6300ca 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,6 +2,9 @@ class Ability def self.allowed(object, subject) case subject.class.name when "Project" then project_abilities(object, subject) + when "Issue" then issue_abilities(object, subject) + when "Note" then note_abilities(object, subject) + when "Snippet" then snippet_abilities(object, subject) else [] end end @@ -12,6 +15,7 @@ class Ability rules << [ :read_project, :read_issue, + :read_snippet, :read_team_member, :read_note ] if project.readers.include?(user) @@ -19,16 +23,35 @@ class Ability rules << [ :write_project, :write_issue, + :write_snippet, :write_note ] if project.writers.include?(user) rules << [ :admin_project, :admin_issue, + :admin_snippet, :admin_team_member, :admin_note ] if project.admins.include?(user) rules.flatten end + + class << self + [:issue, :note, :snippet].each do |name| + define_method "#{name}_abilities" do |user, subject| + if subject.author == user + [ + :"read_#{name}", + :"write_#{name}", + :"admin_#{name}" + ] + else + subject.respond_to?(:project) ? + project_abilities(user, subject.project) : [] + end + end + end + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 9b1b923eaa0..556cdc1c1c9 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -37,5 +37,6 @@ end # created_at :datetime # updated_at :datetime # closed :boolean default(FALSE), not null +# position :integer default(0) # diff --git a/app/models/note.rb b/app/models/note.rb index f89fb9f8995..e3dabce4791 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -22,6 +22,10 @@ class Note < ActiveRecord::Base scope :common, where(:noteable_id => nil) + scope :last_week, where("created_at >= :date", :date => (Date.today - 7.days)) + scope :since, lambda { |day| where("created_at >= :date", :date => (day)) } + scope :fresh, order("created_at DESC") + mount_uploader :attachment, AttachmentUploader end # == Schema Information diff --git a/app/models/project.rb b/app/models/project.rb index 2340940ba5f..d70b18e7934 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -7,6 +7,7 @@ class Project < ActiveRecord::Base has_many :users_projects, :dependent => :destroy has_many :users, :through => :users_projects has_many :notes, :dependent => :destroy + has_many :snippets, :dependent => :destroy validates :name, :uniqueness => true, @@ -125,6 +126,34 @@ class Project < ActiveRecord::Base end end + def heads + @heads ||= repo.heads + end + + def fresh_commits + commits = heads.map do |h| + repo.commits(h.name, 10) + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0..10] + end + + def commits_since(date) + commits = heads.map do |h| + repo.log(h.name, nil, :since => date) + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits + end + def tree(fcommit, path = nil) fcommit = commit if fcommit == :head tree = fcommit.tree diff --git a/app/models/snippet.rb b/app/models/snippet.rb new file mode 100644 index 00000000000..95d6a07d545 --- /dev/null +++ b/app/models/snippet.rb @@ -0,0 +1,51 @@ +class Snippet < ActiveRecord::Base + include Utils::Colorize + + belongs_to :project + belongs_to :author, :class_name => "User" + has_many :notes, :as => :noteable + + attr_protected :author, :author_id, :project, :project_id + + validates_presence_of :project_id + validates_presence_of :author_id + + validates :title, + :presence => true, + :length => { :within => 0..255 } + + validates :file_name, + :presence => true, + :length => { :within => 0..255 } + + validates :content, + :presence => true, + :length => { :within => 0..10000 } + + + def self.content_types + [ + ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java", + ".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb", + ".js", ".sh", ".coffee", ".yml", ".md" + ] + end + + def colorize + system_colorize(content, file_name) + end +end +# == Schema Information +# +# Table name: snippets +# +# id :integer not null, primary key +# title :string(255) +# content :text +# author_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# file_name :string(255) +# + diff --git a/app/models/user.rb b/app/models/user.rb index 0972f006dbd..7736599e7eb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,7 +5,8 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model - attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :projects_limit + attr_accessible :email, :password, :password_confirmation, :remember_me, + :name, :projects_limit, :skype, :linkedin, :twitter has_many :users_projects, :dependent => :destroy has_many :projects, :through => :users_projects @@ -58,5 +59,8 @@ end # name :string(255) # admin :boolean default(FALSE), not null # projects_limit :integer +# skype :string +# linkedin :string +# twitter :string # diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 17be416fa1d..aa9df298e11 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -25,13 +25,26 @@ = f.label :password_confirmation %br = f.password_field :password_confirmation - .span-11 - .field.prepend-top.append-bottom + .field.prepend-top = f.check_box :admin = f.label :admin + .span-11 .field.prepend-top = f.text_field :projects_limit, :class => "small_input" = f.label :projects_limit + + .field + = f.label :skype + %br + = f.text_field :skype + .field + = f.label :linkedin + %br + = f.text_field :linkedin + .field + = f.label :twitter + %br + = f.text_field :twitter .clear %br .actions diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index b1d110bec27..aee73c38ee7 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -14,6 +14,17 @@ %b Projects limit: = @admin_user.projects_limit + %p + %b Skype: + = @admin_user.skype + %p + %b LinkedIn: + = @admin_user.linkedin + %p + %b Twitter: + = @admin_user.twitter + + .clear = link_to 'Edit', edit_admin_user_path(@admin_user) \| diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 4eebb83aa15..94a1bd1b995 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -11,12 +11,12 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p %strong - = commit.message.length > 60 ? (commit.message[0..59] + "...") : commit.message + = truncate_commit_message(commit) = link_to "Browse Code", tree_project_path(@project, :commit_id => commit.id), :class => "lite_button", :style => "float:right" = link_to truncate(commit.id.to_s, :length => 16), project_commit_path(@project, :id => commit.id), :class => "lite_button", :style => "width:120px;float:right" %span - %span - [ #{commit.author} ] + %span.author + = commit.author = time_ago_in_words(commit.committed_date) ago = more_commits_link if @commits.size > 99 diff --git a/app/views/commits/_diff.html.haml b/app/views/commits/_diff.html.haml index dff99bf12f0..73652aaf9f7 100644 --- a/app/views/commits/_diff.html.haml +++ b/app/views/commits/_diff.html.haml @@ -1,4 +1,3 @@ -- require "utils" .file_stats - @commit.diffs.each do |diff| - if diff.deleted_file @@ -35,7 +34,7 @@ %strong{:id => "#{diff.b_path}"}= diff.b_path %br/ .diff_file_content - - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + - if file.text? - lines_arr = diff.diff.lines.to_a - line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 - line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 @@ -50,9 +49,9 @@ - else - line_new += 1 - line_old += 1 - - elsif file.mime_type =~ /image/ + - elsif file.image? .diff_file_content_image - %img{:src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + %img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else %p %center No preview for this file type diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 147aaafb855..a2c9149d06c 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -1,5 +1,5 @@ %h3 - = "[ #{@commit.committer} ] #{truncate @commit.message, :length => 80}" + = "[ #{@commit.committer} ] #{truncate_commit_message(@commit, 80)}" -#= link_to 'Back', project_commits_path(@project), :class => "button" %table.round-borders %tr diff --git a/app/views/commits/show.js.haml b/app/views/commits/show.js.haml index 2c46689b1ed..cec1fe28348 100644 --- a/app/views/commits/show.js.haml +++ b/app/views/commits/show.js.haml @@ -1,6 +1,8 @@ -:plain +-#:plain $("#side-commit-preview").remove(); var side = $("
    "); side.html("#{escape_javascript(render "commits/show")}"); $("##{dom_id(@project)}").parent().append(side); $("##{dom_id(@project)}").addClass("span-14"); +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/issues/show.js.haml b/app/views/issues/show.js.haml new file mode 100644 index 00000000000..5b9a34c0a9c --- /dev/null +++ b/app/views/issues/show.js.haml @@ -0,0 +1,2 @@ +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml index ccb159f128c..ca56a2450a0 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -12,9 +12,9 @@ = f.label :note %cite (255 symbols only) %br - = f.text_area :note, :style => "width:97%;height:100px", :size => 255 + = f.text_area :note, :size => 255 - %div + %div.attach_holder = f.label :attachment %cite (less than 10 MB) %br @@ -25,4 +25,4 @@ .clear %br - = f.submit 'Add note', :class => "lbutton vm" + = f.submit 'Add note', :class => "lbutton vm", :id => "submit_note" diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml index 9d1d4b941c8..2d110162eca 100644 --- a/app/views/notes/_notes.html.haml +++ b/app/views/notes/_notes.html.haml @@ -1,15 +1,28 @@ -%ul#notes-list - - @notes.each do |note| - - next unless note.author - = render :partial => "notes/show", :locals => {:note => note} +- if controller.action_name == "wall" + %ul#notes-list= render "notes/notes_list" -%br -%br -- if can? current_user, :write_note, @project - = render "notes/form" +- else + %ul#notes-list= render "notes/notes_list" + %br + %br + - if can? current_user, :write_note, @project + = render "notes/form" :javascript $('.delete-note').live('ajax:success', function() { $(this).closest('li').fadeOut(); }); + $("#new_note").live("ajax:before", function(){ + $("#submit_note").attr("disabled", "disabled"); + }) + $("#new_note").live("ajax:complete", function(){ + $("#submit_note").removeAttr("disabled"); + }) + + +- if ["issues", "projects"].include?(controller.controller_name) + :javascript + $(function(){ + var int =self.setInterval("updatePage()", 20000); + }); diff --git a/app/views/notes/_notes_list.html.haml b/app/views/notes/_notes_list.html.haml new file mode 100644 index 00000000000..1e4a6bb2b2f --- /dev/null +++ b/app/views/notes/_notes_list.html.haml @@ -0,0 +1,4 @@ +- @notes.each do |note| + - next unless note.author + = render :partial => "notes/show", :locals => {:note => note} + diff --git a/app/views/notes/_show.html.haml b/app/views/notes/_show.html.haml index 2b0a6d2ebcd..ee9f9ffaa4b 100644 --- a/app/views/notes/_show.html.haml +++ b/app/views/notes/_show.html.haml @@ -1,19 +1,17 @@ %li{:id => dom_id(note)} %div.note_author = image_tag gravatar_icon(note.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" - %div.note_content + %div.note_content.left = simple_format(html_escape(note.note)) - if note.attachment.url Attachment: - = link_to note.attachment_identifier, note.attachment.url + = link_to note.attachment_identifier, note.attachment.url, :target => "_blank" %br - %span - %span - [ #{note.author.name} ] -   + %span.author= note.author.name + %cite.ago = time_ago_in_words(note.updated_at) ago - %br + %br - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) = link_to 'Remove', [@project, note], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-note right negative" .clear diff --git a/app/views/notes/create.js.haml b/app/views/notes/create.js.haml index 47cff1d8834..15371dbce2b 100644 --- a/app/views/notes/create.js.haml +++ b/app/views/notes/create.js.haml @@ -1,8 +1,11 @@ - if @note.valid? :plain $("#new_note .errors").remove(); - $("#notes-list").append("#{escape_javascript(render(:partial => 'show', :locals => {:note => @note} ))}"); + updatePage(); $('#note_note').val(""); - else :plain $("#new_note").replaceWith("#{escape_javascript(render('form'))}"); + +:plain + $("#submit_note").removeAttr("disabled"); diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 12737ba8143..ef23a1692a1 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -6,3 +6,28 @@ %p %b Email: = @user.email + +%br + += form_for @user, :url => profile_edit_path, :method => :put do |f| + -if @user.errors.any? + #error_explanation + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .div + = f.label :skype + %br + = f.text_field :skype + .div + = f.label :linkedin + %br + = f.text_field :linkedin + .div + = f.label :twitter + %br + = f.text_field :twitter + .actions + = f.submit 'Save', :class => "lbutton vm" + diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index baa1f14fe06..00ca98efa08 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -1,8 +1,6 @@ = form_for(@project, :remote => true) do |f| %div.form_content - - if @project.new_record? - %h1 New Project - - else + - unless @project.new_record? %h1 Edit Project - if @project.errors.any? #error_explanation @@ -26,7 +24,7 @@ %td .left= f.label :code %cite.right http://yourserver/ - %td= f.text_field :code, :placeholder => "example (3..12 symbols only)" + %td= f.text_field :code, :placeholder => "example" .field = f.label :description %br/ diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml new file mode 100644 index 00000000000..e435ea30f00 --- /dev/null +++ b/app/views/projects/_recent_commits.html.haml @@ -0,0 +1,18 @@ +- @commits.each do |commit| + %div.commit + - if commit.author.email + = image_tag gravatar_icon(commit.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" + - else + = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" + %p{:style => "margin-bottom: 3px;"} + %strong + = link_to truncate_commit_message(commit, 60), project_commit_path(@project, :id => commit.id) + + %span + %span.author + = commit.author.name.force_encoding("UTF-8") + %cite + = time_ago_in_words(commit.committed_date) + ago + %br + diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml new file mode 100644 index 00000000000..1af7fe3bb24 --- /dev/null +++ b/app/views/projects/_recent_messages.html.haml @@ -0,0 +1,52 @@ +- @messages.group_by{ |x| [x.noteable_id, x.noteable_type]}.each do |item, notes| + - id, type = item[0], item[1] + - parent = load_note_parent(id, type, @project) + - next unless parent + + - case type + - when "Issue" + - css_class = "dash_issue" + - issue = parent + - item_code = issue.author.email + - link_item_name = truncate(issue.title, :length => 50) + - link_to_item = project_issue_path(@project, issue) + - when "Snippet" + - css_class = "dash_snippet" + - item_code = parent.author.email + - link_item_name = parent.title + - link_to_item = project_snippet_path(@project, parent) + - when "Commit" + - css_class = "dash_commit" + - commit = parent + - item_code = commit.author.email + - link_item_name = truncate_commit_message(commit, 50) + - link_to_item = project_commit_path(@project, :id => commit.id) + - else + - css_class = "dash_wall" + - item_code = @project.name + - link_item_name = "Project Wall" + - link_to_item = wall_project_path(@project) + + %div{ :class => "recent_message_parent #{css_class}"} + = image_tag gravatar_icon(item_code), :class => "left", :width => 40 + %h4 + = link_to(link_item_name, link_to_item) + %span + = type + .clear + - notes.sort {|x,y| x.updated_at <=> y.updated_at }.each do |note| + %div.message + = image_tag gravatar_icon(note.author.email), :class => "left", :width => 24, :style => "padding-right:5px;" + %p{:style => "margin-bottom: 3px;"} + %span.author + = note.author.name + = link_to truncate(note.note, :length => 200), link_to_item + "#note_#{note.id}" + - if note.attachment.url + %br + Attachment: + = link_to note.attachment_identifier, note.attachment.url + %br + %br + .append-bottom +   + .clear diff --git a/app/views/projects/_tile.html.haml b/app/views/projects/_tile.html.haml index 037aeccb228..b2466d932ce 100644 --- a/app/views/projects/_tile.html.haml +++ b/app/views/projects/_tile.html.haml @@ -4,7 +4,7 @@ %div{ :class => "project", :url => project_path(project) } %h2 = image_tag gravatar_icon(project.name), :class => "left", :width => 40, :style => "padding-right:5px;" - = "/" + project.code + = link_to ("/" + project.code), project_path(project), :style => "text-decoration:none" %p= project.name %p= project.url_to_repo -#%p diff --git a/app/views/projects/_top_menu.html.haml b/app/views/projects/_top_menu.html.haml index b81ba6bb4d6..59f2533ea7f 100644 --- a/app/views/projects/_top_menu.html.haml +++ b/app/views/projects/_top_menu.html.haml @@ -1,10 +1,11 @@ %div.top_project_menu -#%span= link_to @project.code.capitalize, @project, :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil - if @project.repo_exists? - %span= link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) || current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil + %span= link_to image_tag("home.png", :width => 20), project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil + %span= link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil %span= link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil %span - = link_to team_project_path(@project), :class => current_page?(:controller => "projects", :action => "team", :id => @project) ? "current" : nil do + = link_to team_project_path(@project), :class => (current_page?(:controller => "projects", :action => "team", :id => @project) || controller.controller_name == "team_members") ? "current" : nil do Team - if @project.users_projects.count > 0 %span{ :class => "top_menu_count" }= @project.users_projects.count @@ -18,6 +19,11 @@ Wall - if @project.common_notes.count > 0 %span{ :class => "top_menu_count" }= @project.common_notes.count + %span + = link_to project_snippets_path(@project), :class => (controller.controller_name == "snippets") ? "current" : nil do + Snippets + - if @project.snippets.count > 0 + %span{ :class => "top_menu_count" }= @project.snippets.count - if @commit %span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil diff --git a/app/views/projects/_tree_file.html.haml b/app/views/projects/_tree_file.html.haml index 3463bfc543f..41a2287aa8c 100644 --- a/app/views/projects/_tree_file.html.haml +++ b/app/views/projects/_tree_file.html.haml @@ -1,4 +1,4 @@ -- require "utils" +:css .view_file .view_file_header %strong @@ -6,14 +6,13 @@ = link_to "raw", blob_project_path(@project, :commit_id => @commit.id, :path => params[:path] ), :class => "right", :target => "_blank" = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right", :style => "margin-right:10px;" %br/ - - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + - if file.text? .view_file_content - - ft = handle_file_type(file.name, file.mime_type) :erb - <%= raw Albino.colorize(content, ft, :html, 'utf-8', "linenos=True") %> - - elsif file.mime_type =~ /image/ + <%= raw file.colorize %> + - elsif file.image? .view_file_content_image - %img{ :src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else %p %center No preview for this file type diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 9ba33c1ef88..4ebcfbefeab 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -12,4 +12,4 @@ = time_ago_in_words(content_commit.committed_date) ago %td - = link_to truncate(content_commit.message, :length => 40), project_commit_path(@project, content_commit) + = link_to truncate_commit_message(content_commit, 40), project_commit_path(@project, content_commit) diff --git a/app/views/projects/empty.html.erb b/app/views/projects/empty.html.erb index a8917471550..4c60facde8c 100644 --- a/app/views/projects/empty.html.erb +++ b/app/views/projects/empty.html.erb @@ -1,3 +1,4 @@ +<% bash_lexer = Pygments::Lexer[:bash] %>

    Git global setup:

    @@ -6,7 +7,7 @@ git config --global user.name "#{current_user.name}" git config --global user.email "#{current_user.email}" eos %> - <%= raw Albino.colorize(setup_str, :bash) %> + <%= raw bash_lexer.highlight(setup_str) %>

    Next steps:

    @@ -21,7 +22,7 @@ git remote add origin #{@project.url_to_repo} git push -u origin master eos %> - <%= raw Albino.colorize(repo_setup_str, :bash) %> + <%= raw bash_lexer.highlight(repo_setup_str) %>

    Existing Git Repo?

    @@ -31,7 +32,7 @@ git remote add origin #{@project.url_to_repo} git push -u origin master eos %> - <%= raw Albino.colorize(exist_repo_setup_str, :bash) %> + <%= raw bash_lexer.highlight(exist_repo_setup_str) %>

    Remove this project?

    diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 0fd9c8ead19..85019ecbe5d 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,3 +1,23 @@ %div - %div#tree-holder - = render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @commit.tree} + %h2.left History + .right + = form_tag project_path(@project), :method => :get do + .span-2 + = radio_button_tag :view, "recent", (params[:view] || "recent") == "recent", :onclick => "this.form.submit()", :id => "recent_view" + = label_tag "recent_view","Recent" + .span-2 + = radio_button_tag :view, "day", params[:view] == "day", :onclick => "this.form.submit()", :id => "day_view" + = label_tag "day_view","Today" + .span-2 + = radio_button_tag :view, "week", params[:view] == "week", :onclick => "this.form.submit()", :id => "week_view" + = label_tag "week_view","Week" + .clear + %hr +.span-11 + %h3 Commits + =render "projects/recent_commits" + +.span-11.right + %h3 Talk + =render "projects/recent_messages" + diff --git a/app/views/projects/wall.html.haml b/app/views/projects/wall.html.haml index 479bb3cfc5b..ed22478c924 100644 --- a/app/views/projects/wall.html.haml +++ b/app/views/projects/wall.html.haml @@ -1 +1,29 @@ +%div.wall_page + - if can? current_user, :write_note, @project + = render "notes/form" + .right + = form_tag wall_project_path(@project), :method => :get do + .span-2 + = radio_button_tag :view, "recent", (params[:view] || "recent") == "recent", :onclick => "this.form.submit()", :id => "recent_view" + = label_tag "recent_view","Recent" + .span-2 + = radio_button_tag :view, "day", params[:view] == "day", :onclick => "this.form.submit()", :id => "day_view" + = label_tag "day_view","Today" + .span-2 + = radio_button_tag :view, "week", params[:view] == "week", :onclick => "this.form.submit()", :id => "week_view" + = label_tag "week_view","Week" + .span-2 + = radio_button_tag :view, "all", params[:view] == "all", :onclick => "this.form.submit()", :id => "all_view" + = label_tag "all_view","All" + .clear + %br + %hr = render "notes/notes" + +:javascript + $(function(){ + $("#note_note").live("click", function(){ + $(this).css("height", "100px"); + $('.attach_holder').show(); + }); + }); diff --git a/app/views/projects/wall.js.haml b/app/views/projects/wall.js.haml new file mode 100644 index 00000000000..5b9a34c0a9c --- /dev/null +++ b/app/views/projects/wall.js.haml @@ -0,0 +1,2 @@ +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml new file mode 100644 index 00000000000..571e2b063c1 --- /dev/null +++ b/app/views/snippets/_form.html.haml @@ -0,0 +1,22 @@ +%div + = form_for [@project, @snippet] do |f| + -if @snippet.errors.any? + %ul + - @snippet.errors.full_messages.each do |msg| + %li= msg + + %table.round-borders + %tr + %td= f.label :title + %td= f.text_field :title, :placeholder => "Example Snippet" + %tr + %td= f.label :file_name + %td= f.text_field :file_name, :placeholder => "example.rb" + %tr + %td{:colspan => 2} + = f.label :content, "Code" + %br + = f.text_area :content, :style => "height:240px;width:932px;" + + .actions.prepend-top + = f.submit 'Save', :class => "lbutton vm" diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml new file mode 100644 index 00000000000..483ff42cbb6 --- /dev/null +++ b/app/views/snippets/_snippet.html.haml @@ -0,0 +1,11 @@ +%tr{ :id => dom_id(snippet), :class => "snippet", :url => project_snippet_path(@project, snippet) } + %td + = image_tag gravatar_icon(snippet.author.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = truncate snippet.author.name, :lenght => 20 + %td= html_escape snippet.title + %td= html_escape snippet.file_name + %td + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "lbutton positive" + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{snippet.id}" diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml new file mode 100644 index 00000000000..f81c0b8bc64 --- /dev/null +++ b/app/views/snippets/edit.html.haml @@ -0,0 +1 @@ += render "snippets/form" diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml new file mode 100644 index 00000000000..6e5dbde5bb1 --- /dev/null +++ b/app/views/snippets/index.html.haml @@ -0,0 +1,14 @@ +%div + - if can? current_user, :write_snippet, @project + .left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm" + + %table.round-borders#snippets-table + %tr + %th Author + %th Title + %th File name + %th + = render @snippets +:javascript + $('.delete-snippet').live('ajax:success', function() { + $(this).closest('tr').fadeOut(); }); diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml new file mode 100644 index 00000000000..f81c0b8bc64 --- /dev/null +++ b/app/views/snippets/new.html.haml @@ -0,0 +1 @@ += render "snippets/form" diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml new file mode 100644 index 00000000000..899950b7c36 --- /dev/null +++ b/app/views/snippets/show.html.haml @@ -0,0 +1,22 @@ +%h2 + = "Snippet ##{@snippet.id} - #{@snippet.title}" + +.view_file + .view_file_header + %strong + = @snippet.file_name + %br/ + .view_file_content + :erb + <%= raw @snippet.colorize %> + +- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user + = link_to 'Edit', edit_project_snippet_path(@project, @snippet), :class => "lbutton positive" +- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user + = link_to 'Destroy', [@project, @snippet], :confirm => 'Are you sure?', :method => :delete, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{@snippet.id}" +.clear +%br +.snippet_notes= render "notes/notes" + +.clear + diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml index 6d310768afa..b9a68e6cfb0 100644 --- a/app/views/team_members/_show.html.haml +++ b/app/views/team_members/_show.html.haml @@ -1,8 +1,10 @@ - user = member.user %tr{:id => dom_id(member)} %td - = image_tag gravatar_icon(user.email), :class => "left", :width => 40, :style => "padding:0 5px;" - = truncate user.name, :lenght => 16 + = link_to image_tag(gravatar_icon(user.email), :class => "left", :width => 40, :style => "padding:0 5px;"), project_team_member_path(@project, member) + + = link_to truncate(user.name, :lenght => 16), project_team_member_path(@project, member) + %td= truncate user.email, :lenght => 16 - if can? current_user, :admin_project, @project = form_for(member, :as => :team_member, :url => project_team_member_path(@project, member)) do |f| diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml new file mode 100644 index 00000000000..d07c54f8b68 --- /dev/null +++ b/app/views/team_members/show.html.haml @@ -0,0 +1,28 @@ +- user = @team_member.user +.span-2 + = image_tag gravatar_icon(user.email), :class => "left", :width => 60, :style => "padding-right:5px;" +%p + %b Name: + = user.name +%p + %b Email: + = user.email + +%br + +- unless user.skype.empty? + .div + %b Skype: + = user.skype + +- unless user.linkedin.empty? + .div + %b LinkedIn: + = user.linkedin + +- unless user.twitter.empty? + .div + %b Twitter: + = user.twitter + + diff --git a/config/initializers/grit_ext.rb b/config/initializers/grit_ext.rb new file mode 100644 index 00000000000..4bd71003532 --- /dev/null +++ b/config/initializers/grit_ext.rb @@ -0,0 +1,8 @@ +require 'grit' +require 'pygments' +require "utils" + +Grit::Blob.class_eval do + include Utils::FileHelper + include Utils::Colorize +end diff --git a/config/routes.rb b/config/routes.rb index acf92536e3f..8a40a8fef89 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,6 +13,7 @@ Gitlab::Application.routes.draw do get "errors/gitosis" get "profile/password", :to => "profile#password" put "profile/password", :to => "profile#password_update" + put "profile/edit", :to => "profile#social_update" get "profile", :to => "profile#show" #get "profile/:id", :to => "profile#show" @@ -38,6 +39,8 @@ Gitlab::Application.routes.draw do } end + + resources :snippets resources :commits resources :team_members resources :issues do diff --git a/db/migrate/20111016183422_create_snippets.rb b/db/migrate/20111016183422_create_snippets.rb new file mode 100644 index 00000000000..9b0bf201cba --- /dev/null +++ b/db/migrate/20111016183422_create_snippets.rb @@ -0,0 +1,12 @@ +class CreateSnippets < ActiveRecord::Migration + def change + create_table :snippets do |t| + t.string :title + t.text :content + t.integer :author_id, :null => false + t.integer :project_id, :null => false + + t.timestamps + end + end +end diff --git a/db/migrate/20111016193417_add_content_type_to_snippets.rb b/db/migrate/20111016193417_add_content_type_to_snippets.rb new file mode 100644 index 00000000000..511a6793f4f --- /dev/null +++ b/db/migrate/20111016193417_add_content_type_to_snippets.rb @@ -0,0 +1,5 @@ +class AddContentTypeToSnippets < ActiveRecord::Migration + def change + add_column :snippets, :content_type, :string, :null => false, :default => "txt" + end +end diff --git a/db/migrate/20111016195506_add_file_name_to_snippets.rb b/db/migrate/20111016195506_add_file_name_to_snippets.rb new file mode 100644 index 00000000000..d378d225ec1 --- /dev/null +++ b/db/migrate/20111016195506_add_file_name_to_snippets.rb @@ -0,0 +1,6 @@ +class AddFileNameToSnippets < ActiveRecord::Migration + def change + add_column :snippets, :file_name, :string + remove_column :snippets, :content_type + end +end diff --git a/db/migrate/20111019212429_add_social_to_user.rb b/db/migrate/20111019212429_add_social_to_user.rb new file mode 100644 index 00000000000..b0ffe5366a4 --- /dev/null +++ b/db/migrate/20111019212429_add_social_to_user.rb @@ -0,0 +1,7 @@ +class AddSocialToUser < ActiveRecord::Migration + def change + add_column :users, :skype, :string + add_column :users, :linkedin, :string + add_column :users, :twitter, :string + end +end diff --git a/db/migrate/20111021101550_change_social_fields_in_users.rb b/db/migrate/20111021101550_change_social_fields_in_users.rb new file mode 100644 index 00000000000..6e506c1cf9d --- /dev/null +++ b/db/migrate/20111021101550_change_social_fields_in_users.rb @@ -0,0 +1,14 @@ +class ChangeSocialFieldsInUsers < ActiveRecord::Migration + def up + remove_column :users, :skype + remove_column :users, :linkedin + remove_column :users, :twitter + + add_column :users, :skype, :string, {:null => false, :default => ''} + add_column :users, :linkedin, :string, {:null => false, :default => ''} + add_column :users, :twitter, :string, {:null => false, :default => ''} + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index ed37dbbb923..cc805990a8b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111015154310) do +ActiveRecord::Schema.define(:version => 20111021101550) do create_table "issues", :force => true do |t| t.string "title" @@ -56,6 +56,16 @@ ActiveRecord::Schema.define(:version => 20111015154310) do t.integer "owner_id" end + create_table "snippets", :force => true do |t| + t.string "title" + t.text "content" + t.integer "author_id", :null => false + t.integer "project_id", :null => false + t.datetime "created_at" + t.datetime "updated_at" + t.string "file_name" + end + create_table "users", :force => true do |t| t.string "email", :default => "", :null => false t.string "encrypted_password", :limit => 128, :default => "", :null => false @@ -72,6 +82,9 @@ ActiveRecord::Schema.define(:version => 20111015154310) do t.string "name" t.boolean "admin", :default => false, :null => false t.integer "projects_limit", :default => 10 + t.string "skype", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false end add_index "users", ["email"], :name => "index_users_on_email", :unique => true diff --git a/lib/color.rb b/lib/color.rb index d5500aca8e2..23feecf4071 100644 --- a/lib/color.rb +++ b/lib/color.rb @@ -11,6 +11,10 @@ module Color colorize(text, "32m") end + def yellow(text) + colorize(text, "93m") + end + def command(string) `#{string}` if $?.to_i > 0 diff --git a/lib/gitosis.rb b/lib/gitosis.rb index 4cc5e6e3d08..cab7da2d435 100644 --- a/lib/gitosis.rb +++ b/lib/gitosis.rb @@ -27,13 +27,11 @@ class Gitosis def configure status = Timeout::timeout(20) do File.open(File.join(Dir.tmpdir,"gitlabhq-gitosis.lock"), "w+") do |f| - begin + begin f.flock(File::LOCK_EX) - pull yield(self) push - ensure f.flock(File::LOCK_UN) end diff --git a/lib/utils.rb b/lib/utils.rb index 6e7460ed99a..e57121a3e2a 100644 --- a/lib/utils.rb +++ b/lib/utils.rb @@ -1,8 +1,51 @@ module Utils - def self.binary?(string) - string.each_byte do |x| - x.nonzero? or return true + module FileHelper + def binary?(string) + string.each_byte do |x| + x.nonzero? or return true + end + false + end + + def image? + mime_type =~ /image/ + end + + def text? + mime_type =~ /application|text/ && !binary?(data) + end + end + + module Colorize + def colorize + system_colorize(data, name) + end + + def system_colorize(data, file_name) + ft = handle_file_type(file_name) + Pygments.highlight(data, :lexer => ft, :options => { :encoding => 'utf-8', :linenos => 'True' }) + end + + def handle_file_type(file_name, mime_type = nil) + if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ + :ruby + elsif file_name =~ /\.py$/ + :python + elsif file_name =~ /(\.pl|\.scala|\.c|\.cpp|\.java|\.haml|\.html|\.sass|\.scss|\.xml|\.php|\.erb)$/ + $1[1..-1].to_sym + elsif file_name =~ /\.js$/ + :javascript + elsif file_name =~ /\.sh$/ + :bash + elsif file_name =~ /\.coffee$/ + :coffeescript + elsif file_name =~ /\.yml$/ + :yaml + elsif file_name =~ /\.md$/ + :minid + else + :text + end end - false end end diff --git a/spec/factories.rb b/spec/factories.rb index ea055d1bcd0..cc0cd4e5e8f 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -35,6 +35,12 @@ Factory.add(:issue, Issue) do |obj| obj.content = Faker::Lorem.sentences end +Factory.add(:snippet, Snippet) do |obj| + obj.title = Faker::Lorem.sentence + obj.file_name = Faker::Lorem.sentence + obj.content = Faker::Lorem.sentences +end + Factory.add(:note, Note) do |obj| obj.note = Faker::Lorem.sentence end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 23609023c50..b2d594c904d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -38,5 +38,6 @@ end # created_at :datetime # updated_at :datetime # closed :boolean default(FALSE), not null +# position :integer default(0) # diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb new file mode 100644 index 00000000000..9dab72ca7dc --- /dev/null +++ b/spec/models/snippet_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Snippet do + describe "Associations" do + it { should belong_to(:project) } + it { should belong_to(:author) } + end + + describe "Validation" do + it { should validate_presence_of(:title) } + it { should validate_presence_of(:author_id) } + it { should validate_presence_of(:project_id) } + it { should validate_presence_of(:file_name) } + it { should validate_presence_of(:content) } + end +end +# == Schema Information +# +# Table name: snippets +# +# id :integer not null, primary key +# title :string(255) +# content :text +# author_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# file_name :string(255) +# + diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 77abe2ca935..32fb90a317c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -39,5 +39,8 @@ end # name :string(255) # admin :boolean default(FALSE), not null # projects_limit :integer +# skype :string +# linkedin :string +# twitter :string # diff --git a/spec/requests/profile_spec.rb b/spec/requests/profile_spec.rb index 07fdc4abdc6..5838f63b40b 100644 --- a/spec/requests/profile_spec.rb +++ b/spec/requests/profile_spec.rb @@ -14,6 +14,22 @@ describe "Profile" do it { page.should have_content(@user.email) } end + describe "Profile update" do + before do + visit profile_path + fill_in "user_skype", :with => "testskype" + fill_in "user_linkedin", :with => "testlinkedin" + fill_in "user_twitter", :with => "testtwitter" + click_button "Save" + @user.reload + end + + it { @user.skype.should == 'testskype' } + it { @user.linkedin.should == 'testlinkedin' } + it { @user.twitter.should == 'testtwitter' } + end + + describe "Password update" do before do visit profile_password_path diff --git a/spec/requests/projects_security_spec.rb b/spec/requests/projects_security_spec.rb index a725a49c2d8..90f88d88085 100644 --- a/spec/requests/projects_security_spec.rb +++ b/spec/requests/projects_security_spec.rb @@ -82,12 +82,18 @@ describe "Projects" do end describe "GET /project_code/blob" do - it { blob_project_path(@project).should be_allowed_for @u1 } - it { blob_project_path(@project).should be_allowed_for @u3 } - it { blob_project_path(@project).should be_denied_for :admin } - it { blob_project_path(@project).should be_denied_for @u2 } - it { blob_project_path(@project).should be_denied_for :user } - it { blob_project_path(@project).should be_denied_for :visitor } + before do + @commit = @project.commit + @path = @commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name + @blob_path = blob_project_path(@project, :commit_id => @commit.id, :path => @path) + end + + it { @blob_path.should be_allowed_for @u1 } + it { @blob_path.should be_allowed_for @u3 } + it { @blob_path.should be_denied_for :admin } + it { @blob_path.should be_denied_for @u2 } + it { @blob_path.should be_denied_for :user } + it { @blob_path.should be_denied_for :visitor } end describe "GET /project_code/edit" do @@ -107,5 +113,14 @@ describe "Projects" do it { project_issues_path(@project).should be_denied_for :user } it { project_issues_path(@project).should be_denied_for :visitor } end + + describe "GET /project_code/snippets" do + it { project_snippets_path(@project).should be_allowed_for @u1 } + it { project_snippets_path(@project).should be_allowed_for @u3 } + it { project_snippets_path(@project).should be_denied_for :admin } + it { project_snippets_path(@project).should be_denied_for @u2 } + it { project_snippets_path(@project).should be_denied_for :user } + it { project_snippets_path(@project).should be_denied_for :visitor } + end end end diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb index 2825a6a979a..945c1ea26f5 100644 --- a/spec/requests/projects_spec.rb +++ b/spec/requests/projects_spec.rb @@ -72,7 +72,10 @@ describe "Projects" do current_path.should == project_path(@project) end - it_behaves_like :tree_view + it "should beahave like dashboard" do + page.should have_content("History") + end + end describe "GET /projects/team" do @@ -134,8 +137,6 @@ describe "Projects" do it "should show project" do page.should have_content("Awesome") end - - it_behaves_like :tree_view end #describe "DELETE /projects/:id", :js => true do diff --git a/spec/requests/snippets_spec.rb b/spec/requests/snippets_spec.rb new file mode 100644 index 00000000000..00ae58dad87 --- /dev/null +++ b/spec/requests/snippets_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' + +describe "Snippets" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "GET /snippets" do + before do + @snippet = Factory :snippet, + :author => @user, + :project => project + + visit project_snippets_path(project) + end + + subject { page } + + it { should have_content(@snippet.title) } + it { should have_content(@snippet.project.name) } + it { should have_content(@snippet.author.name) } + + describe "Destroy" do + before do + # admin access to remove snippet + @user.users_projects.destroy_all + project.add_access(@user, :read, :write, :admin) + visit project_snippets_path(project) + end + + it "should remove entry" do + expect { + click_link "destroy_snippet_#{@snippet.id}" + }.to change { Snippet.count }.by(-1) + end + end + end + + describe "New snippet" do + before do + visit project_snippets_path(project) + click_link "New Snippet" + end + + it "should open new snippet popup" do + page.current_path.should == new_project_snippet_path(project) + end + + describe "fill in" do + before do + fill_in "snippet_title", :with => "login function" + fill_in "snippet_file_name", :with => "test.rb" + fill_in "snippet_content", :with => "def login; end" + end + + it { expect { click_button "Save" }.to change {Snippet.count}.by(1) } + + it "should add new snippet to table" do + click_button "Save" + page.current_path.should == project_snippet_path(project, Snippet.last) + page.should have_content "login function" + page.should have_content "test.rb" + end + end + end + + describe "Edit snippet" do + before do + @snippet = Factory :snippet, + :author => @user, + :project => project + visit project_snippets_path(project) + click_link "Edit" + end + + it "should open edit page" do + page.current_path.should == edit_project_snippet_path(project, @snippet) + end + + describe "fill in" do + before do + fill_in "snippet_title", :with => "login function" + fill_in "snippet_file_name", :with => "test.rb" + fill_in "snippet_content", :with => "def login; end" + end + + it { expect { click_button "Save" }.to_not change {Snippet.count} } + + it "should update snippet fields" do + click_button "Save" + + page.current_path.should == project_snippet_path(project, @snippet) + page.should have_content "login function" + page.should have_content "test.rb" + end + end + end +end diff --git a/spec/requests/team_members_spec.rb b/spec/requests/team_members_spec.rb index db7513ae956..dd92febf700 100644 --- a/spec/requests/team_members_spec.rb +++ b/spec/requests/team_members_spec.rb @@ -7,6 +7,15 @@ describe "TeamMembers" do @project.add_access(@user, :read, :admin) end + describe "View profile" do + it "should be available" do + visit(team_project_path(@project)) + find(:xpath, "//table[@id='team-table']//a[1]").click + page.should have_content @user.skype + page.should_not have_content 'Twitter' + end + end + describe "New Team member", :js => true do before do @user_1 = Factory :user diff --git a/spec/requests/user_security_spec.rb b/spec/requests/user_security_spec.rb index 3c923870e86..a27eb1ca33b 100644 --- a/spec/requests/user_security_spec.rb +++ b/spec/requests/user_security_spec.rb @@ -7,10 +7,10 @@ describe "Users Security" do end describe "GET /login" do - it { new_user_session_path.should be_denied_for @u1 } - it { new_user_session_path.should be_denied_for :admin } - it { new_user_session_path.should be_denied_for :user } - it { new_user_session_path.should be_allowed_for :visitor } + #it { new_user_session_path.should be_denied_for @u1 } + #it { new_user_session_path.should be_denied_for :admin } + #it { new_user_session_path.should be_denied_for :user } + it { new_user_session_path.should_not be_404_for :visitor } end describe "GET /keys" do diff --git a/spec/support/login.rb b/spec/support/login.rb index 09f64f9e3c3..462647ab856 100644 --- a/spec/support/login.rb +++ b/spec/support/login.rb @@ -3,7 +3,8 @@ module LoginMacros @user = User.create(:email => "user#{User.count}@mail.com", :name => "John Smith", :password => "123456", - :password_confirmation => "123456") + :password_confirmation => "123456", + :skype => 'user_skype') if role == :admin @user.admin = true diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 953b535698d..dcdfa6d50ff 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -21,17 +21,30 @@ RSpec::Matchers.define :be_denied_for do |user| end end +RSpec::Matchers.define :be_404_for do |user| + match do |url| + include UrlAccess + url_404?(user, url) + end +end + module UrlAccess def url_allowed?(user, url) emulate_user(user) visit url - result = (current_path == url) + (page.status_code != 404 && current_path != new_user_session_path) end def url_denied?(user, url) emulate_user(user) visit url - result = (current_path != url) + (page.status_code == 404 || current_path == new_user_session_path) + end + + def url_404?(user, url) + emulate_user(user) + visit url + page.status_code == 404 end def emulate_user(user) diff --git a/update.rb b/update.rb index d7eec2898b2..b81a4088887 100644 --- a/update.rb +++ b/update.rb @@ -2,6 +2,10 @@ root_path = File.expand_path(File.dirname(__FILE__)) require File.join(root_path, "lib", "color") include Color +def version + File.read("VERSION") +end + # # ruby ./update.rb development # or test or production (default) # @@ -12,15 +16,29 @@ env = if envs.include?(ARGV[0]) "production" end -puts green " == Update for ENV=#{env}" +puts yellow "== RAILS ENV | #{env}" +current_version = version +puts yellow "Your version is #{current_version}" +puts yellow "Check for new version: $ git pull origin 1x" +`git pull origin 1x` # pull from origin -# pull from github -`git pull` +# latest version +if version == current_version + puts yellow "You have a latest version" +else + puts green "Update to #{version}" `bundle install` -# migrate db + # migrate db +if env == "development" +`bundle exec rake db:migrate RAILS_ENV=development` +`bundle exec rake db:migrate RAILS_ENV=test` +else `bundle exec rake db:migrate RAILS_ENV=#{env}` +end + + puts green "== Done! Now you can start/restart server" +end -puts green " == Done! Now you can start/restart server" -- cgit v1.2.1 From ba8048d71019b5aaa1f92ee5c3415bfddaa9babb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 22 Oct 2011 16:07:26 +0300 Subject: v1.1.0 --- README.rdoc | 20 ++++++++++++-- app/assets/stylesheets/projects.css.scss | 9 ++++++ app/controllers/team_members_controller.rb | 28 +++++-------------- app/models/project.rb | 9 +++++- app/models/users_project.rb | 7 ++++- app/views/commits/_diff.html.haml | 44 ++++-------------------------- app/views/commits/_diff_head.html.haml | 24 ++++++++++++++++ app/views/commits/_text_file.html.haml | 15 ++++++++++ db/fixtures/development/001_admin.rb | 10 ++++++- db/fixtures/production/001_admin.rb | 9 ++++++ install.rb | 32 ---------------------- lib/gitosis.rb | 2 +- spec/models/project_spec.rb | 9 ++++++ spec/requests/team_members_spec.rb | 26 +++++++++++++++--- update.rb | 44 ------------------------------ 15 files changed, 141 insertions(+), 147 deletions(-) create mode 100644 app/views/commits/_diff_head.html.haml create mode 100644 app/views/commits/_text_file.html.haml delete mode 100644 install.rb delete mode 100644 update.rb diff --git a/README.rdoc b/README.rdoc index dc74969324d..4016ebba5b8 100644 --- a/README.rdoc +++ b/README.rdoc @@ -28,26 +28,34 @@ sqlite as default db git clone git://github.com/gitlabhq/gitlabhq.git + cd gitlabhq/ # install this library first sudo easy_install pygments + + # give your user access to remove git repo + # Ex. + # If you are going to use user 'gitlabhq' for rails server + # gitlabhq ALL = (git) NOPASSWD: /bin/rm" | sudo tee -a /etc/sudoers + # + echo "USERNAME ALL = (git) NOPASSWD: /bin/rm" | sudo tee -a /etc/sudoers sudo gem install bundler + bundle - RAILS_ENV=production rake db:setup + bundle exec rake db:setup RAILS_ENV=production # create admin user # login....admin@local.host # pass.....5iveL!fe - RAILS_ENV=production rake db:seed_fu + bundle exec rake db:seed_fu RAILS_ENV=production Install gitosis, edit conf/gitosis.yml & start server rails s -e production - == Install Gitosis sudo aptitude install gitosis @@ -64,6 +72,7 @@ Install gitosis, edit conf/gitosis.yml & start server ssh-keygen -t rsa sudo -H -u git gitosis-init < ~/.ssh/id_rsa.pub + sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update @@ -84,3 +93,8 @@ Install gitosis, edit conf/gitosis.yml & start server echo "gem: --no-rdoc --no-ri" > ~/.gemrc + +== Contribute + +We develop project on our private server. +Want to help? Contact us on twitter or email to become a team member. diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 517886722c1..8caa2810e78 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -638,3 +638,12 @@ tbody tr:nth-child(2n) td, tbody tr.even td { display:none; } } + +.field_with_errors { + input[type="text"], + input[type="password"], + textarea + { + background: none repeat scroll 0 0 #FFBBBB + } +} diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index 5fb2710dad7..84addf8d7bb 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -8,35 +8,16 @@ class TeamMembersController < ApplicationController def show @team_member = project.users_projects.find(params[:id]) - - respond_to do |format| - format.html # show.html.erb - format.js - end end def new @team_member = project.users_projects.new - - respond_to do |format| - format.html # new.html.erb - format.js - end end def create @team_member = UsersProject.new(params[:team_member]) @team_member.project = project - - respond_to do |format| - if @team_member.save - format.html { redirect_to @team_member, notice: 'Team member was successfully created.' } - format.js - else - format.html { render action: "new" } - format.js - end - end + @team_member.save end def update @@ -45,7 +26,12 @@ class TeamMembersController < ApplicationController respond_to do |format| format.js - format.html { redirect_to team_project_path(@project)} + format.html do + unless @team_member.valid? + flash[:alert] = "User should have at least one role" + end + redirect_to team_project_path(@project) + end end end diff --git a/app/models/project.rb b/app/models/project.rb index d70b18e7934..5262e55245d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -35,7 +35,8 @@ class Project < ActiveRecord::Base :presence => true validate :check_limit - + validate :repo_name + after_destroy :destroy_gitosis_project after_save :update_gitosis_project @@ -168,6 +169,12 @@ class Project < ActiveRecord::Base errors[:base] << ("Cant check your ability to create project") end + def repo_name + if path == "gitosis-admin" + errors.add(:path, " like 'gitosis-admin' is not allowed") + end + end + def valid_repo? repo rescue diff --git a/app/models/users_project.rb b/app/models/users_project.rb index bdc10633a3e..96e2d16aff0 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -9,7 +9,8 @@ class UsersProject < ActiveRecord::Base validates_uniqueness_of :user_id, :scope => [:project_id] validates_presence_of :user_id validates_presence_of :project_id - + validate :user_has_a_role_selected + delegate :name, :email, :to => :user, :prefix => true def update_gitosis_project @@ -18,6 +19,10 @@ class UsersProject < ActiveRecord::Base end end + def user_has_a_role_selected + errors.add(:base, "Please choose at least one Role in the Access list") unless read || write || admin + end + end # == Schema Information # diff --git a/app/views/commits/_diff.html.haml b/app/views/commits/_diff.html.haml index 73652aaf9f7..2807e0903da 100644 --- a/app/views/commits/_diff.html.haml +++ b/app/views/commits/_diff.html.haml @@ -1,27 +1,5 @@ -.file_stats - - @commit.diffs.each do |diff| - - if diff.deleted_file - %span.removed_file - %a{:href => "##{diff.a_path}"} - = diff.a_path - = image_tag "blueprint_delete.png" - - elsif diff.renamed_file - %span.moved_file - %a{:href => "##{diff.b_path}"} - = diff.a_path - = "->" - = diff.b_path - = image_tag "blueprint_notice.png" - - elsif diff.new_file - %span.new_file - %a{:href => "##{diff.b_path}"} - = diff.b_path - = image_tag "blueprint_add.png" - - else - %span.edit_file - %a{:href => "##{diff.b_path}"} - = diff.b_path - = image_tag "blueprint_info.png" +.file_stats= render "commits/diff_head" + - @commit.diffs.each do |diff| - next if diff.diff.empty? - file = (@commit.tree / diff.b_path) @@ -31,24 +9,12 @@ - if diff.deleted_file %strong{:id => "#{diff.b_path}"}= diff.a_path - else - %strong{:id => "#{diff.b_path}"}= diff.b_path + = link_to tree_file_project_path(@project, @commit.id, diff.b_path) do + %strong{:id => "#{diff.b_path}"}= diff.b_path %br/ .diff_file_content - if file.text? - - lines_arr = diff.diff.lines.to_a - - line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 - - line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 - - lines = lines_arr[3..-1].join - - lines.each_line do |line| - = diff_line(line, line_new, line_old) - - if line[0] == "+" - - line_new += 1 - - elsif - - line[0] == "-" - - line_old += 1 - - else - - line_new += 1 - - line_old += 1 + = render :partial => "commits/text_file", :locals => { :diff => diff } - elsif file.image? .diff_file_content_image %img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} diff --git a/app/views/commits/_diff_head.html.haml b/app/views/commits/_diff_head.html.haml new file mode 100644 index 00000000000..922c35990af --- /dev/null +++ b/app/views/commits/_diff_head.html.haml @@ -0,0 +1,24 @@ +- @commit.diffs.each do |diff| + - if diff.deleted_file + %span.removed_file + %a{:href => "##{diff.a_path}"} + = diff.a_path + = image_tag "blueprint_delete.png" + - elsif diff.renamed_file + %span.moved_file + %a{:href => "##{diff.b_path}"} + = diff.a_path + = "->" + = diff.b_path + = image_tag "blueprint_notice.png" + - elsif diff.new_file + %span.new_file + %a{:href => "##{diff.b_path}"} + = diff.b_path + = image_tag "blueprint_add.png" + - else + %span.edit_file + %a{:href => "##{diff.b_path}"} + = diff.b_path + = image_tag "blueprint_info.png" + diff --git a/app/views/commits/_text_file.html.haml b/app/views/commits/_text_file.html.haml new file mode 100644 index 00000000000..b20aa8afe40 --- /dev/null +++ b/app/views/commits/_text_file.html.haml @@ -0,0 +1,15 @@ +- lines_arr = diff.diff.lines.to_a +- line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 +- line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 +- lines = lines_arr[3..-1].join +- lines.each_line do |line| + = diff_line(line, line_new, line_old) + - if line[0] == "+" + - line_new += 1 + - elsif + - line[0] == "-" + - line_old += 1 + - else + - line_new += 1 + - line_old += 1 + diff --git a/db/fixtures/development/001_admin.rb b/db/fixtures/development/001_admin.rb index 9c5a6da09e0..cfff6bf8bc2 100644 --- a/db/fixtures/development/001_admin.rb +++ b/db/fixtures/development/001_admin.rb @@ -1,4 +1,3 @@ -# Admin account admin = User.create( :email => "admin@local.host", :name => "Administrator", @@ -9,3 +8,12 @@ admin = User.create( admin.projects_limit = 10000 admin.admin = true admin.save! + +if admin.valid? +puts %q[ +Administrator account created: + +login.........admin@local.host +password......5iveL!fe +] +end diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index 94d1abe87ce..cfff6bf8bc2 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -8,3 +8,12 @@ admin = User.create( admin.projects_limit = 10000 admin.admin = true admin.save! + +if admin.valid? +puts %q[ +Administrator account created: + +login.........admin@local.host +password......5iveL!fe +] +end diff --git a/install.rb b/install.rb deleted file mode 100644 index a118cb5d6f1..00000000000 --- a/install.rb +++ /dev/null @@ -1,32 +0,0 @@ -root_path = File.expand_path(File.dirname(__FILE__)) -require File.join(root_path, "lib", "color") -include Color - -# -# ruby ./update.rb development # or test or production (default) -# -envs = ["production", "test", "development"] -env = if envs.include?(ARGV[0]) - ARGV[0] - else - "production" - end - -puts green " == Install for ENV=#{env} ..." - -# bundle install -`bundle install` - -# migrate db -`bundle exec rake db:create RAILS_ENV=#{env}` -`bundle exec rake db:schema:load RAILS_ENV=#{env}` -`bundle exec rake db:seed_fu RAILS_ENV=#{env}` - -puts green %q[ -Administrator account created: - -login.........admin@local.host -password......5iveL!fe -] - -puts green " == Done! Now you can start server" diff --git a/lib/gitosis.rb b/lib/gitosis.rb index cab7da2d435..92d32d8bf73 100644 --- a/lib/gitosis.rb +++ b/lib/gitosis.rb @@ -42,7 +42,7 @@ class Gitosis end def destroy_project(project) - FileUtils.rm_rf(project.path_to_repo) + `sudo -u git rm -rf #{project.path_to_repo}` conf = IniFile.new(File.join(@local_dir,'gitosis','gitosis.conf')) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6ed8653d761..0c62743d8e8 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -4,11 +4,15 @@ describe Project do describe "Associations" do it { should have_many(:users) } it { should have_many(:users_projects) } + it { should have_many(:issues) } + it { should have_many(:notes) } + it { should have_many(:snippets) } end describe "Validation" do it { should validate_presence_of(:name) } it { should validate_presence_of(:path) } + it { should validate_presence_of(:code) } end describe "Respond to" do @@ -31,6 +35,11 @@ describe Project do it { should respond_to(:commit) } end + it "should not allow 'gitosis-admin' as repo name" do + should allow_value("blah").for(:path) + should_not allow_value("gitosis-admin").for(:path) + end + it "should return valid url to repo" do project = Project.new(:path => "somewhere") project.url_to_repo.should == "git@localhost:somewhere.git" diff --git a/spec/requests/team_members_spec.rb b/spec/requests/team_members_spec.rb index dd92febf700..ec0af3214a9 100644 --- a/spec/requests/team_members_spec.rb +++ b/spec/requests/team_members_spec.rb @@ -29,19 +29,37 @@ describe "TeamMembers" do describe "fill in" do before do - check "team_member_read" click_link "Select user" click_link @user_1.name - #select @user_1.name, :from => "team_member_user_id" + + within "#team_member_new" do + check "team_member_read" + check "team_member_write" + end end - it { expect { click_button "Save" }.to change {UsersProject.count}.by(1) } + it { expect { click_button "Save";sleep(1) }.to change {UsersProject.count}.by(1) } it "should add new member to table" do click_button "Save" + @member = UsersProject.last - page.should_not have_content("Add new member") page.should have_content @user_1.name + + @member.read.should be_true + @member.write.should be_true + @member.admin.should be_false + end + + it "should not allow creation without access selected" do + within "#team_member_new" do + uncheck "team_member_read" + uncheck "team_member_write" + uncheck "team_member_admin" + end + + expect { click_button "Save" }.to_not change {UsersProject.count} + page.should have_content("Please choose at least one Role in the Access list") end end end diff --git a/update.rb b/update.rb deleted file mode 100644 index b81a4088887..00000000000 --- a/update.rb +++ /dev/null @@ -1,44 +0,0 @@ -root_path = File.expand_path(File.dirname(__FILE__)) -require File.join(root_path, "lib", "color") -include Color - -def version - File.read("VERSION") -end - -# -# ruby ./update.rb development # or test or production (default) -# -envs = ["production", "test", "development"] -env = if envs.include?(ARGV[0]) - ARGV[0] - else - "production" - end - -puts yellow "== RAILS ENV | #{env}" -current_version = version -puts yellow "Your version is #{current_version}" -puts yellow "Check for new version: $ git pull origin 1x" -`git pull origin 1x` # pull from origin - -# latest version -if version == current_version - puts yellow "You have a latest version" -else - puts green "Update to #{version}" - -`bundle install` - - # migrate db -if env == "development" -`bundle exec rake db:migrate RAILS_ENV=development` -`bundle exec rake db:migrate RAILS_ENV=test` -else -`bundle exec rake db:migrate RAILS_ENV=#{env}` -end - - puts green "== Done! Now you can start/restart server" -end - - -- cgit v1.2.1 From 016012b14598f65aedec6f859bc47da7b7311ae7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 24 Oct 2011 22:47:53 +0300 Subject: Fixed: Commit message encoding error --- app/helpers/commits_helper.rb | 9 --------- app/views/commits/_commits.html.haml | 2 +- app/views/commits/show.html.haml | 2 +- app/views/projects/_recent_commits.html.haml | 2 +- app/views/projects/_recent_messages.html.haml | 2 +- app/views/projects/_tree.html.haml | 8 ++++---- app/views/projects/_tree_file.html.haml | 4 ++-- app/views/projects/_tree_item.html.haml | 2 +- config/initializers/grit_ext.rb | 4 ++++ lib/commit_ext.rb | 9 +++++++++ 10 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 lib/commit_ext.rb diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index b79e5718194..f1b546684f0 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -21,13 +21,4 @@ module CommitsHelper link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit), :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link" end - - # Cause some errors with trucate & encoding use this method - def truncate_commit_message(commit, size = 60) - message = commit.message - message.length > size ? (message[0..(size - 1)] + "...") : message - # if special characters occurs - rescue - commit.message - end end diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 94a1bd1b995..3ed70f1cffb 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -11,7 +11,7 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p %strong - = truncate_commit_message(commit) + = commit.truncated_message = link_to "Browse Code", tree_project_path(@project, :commit_id => commit.id), :class => "lite_button", :style => "float:right" = link_to truncate(commit.id.to_s, :length => 16), project_commit_path(@project, :id => commit.id), :class => "lite_button", :style => "width:120px;float:right" %span diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index a2c9149d06c..0ba42618470 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -1,5 +1,5 @@ %h3 - = "[ #{@commit.committer} ] #{truncate_commit_message(@commit, 80)}" + = "[ #{@commit.committer} ] #{@commit.truncated_message(40)}" -#= link_to 'Back', project_commits_path(@project), :class => "button" %table.round-borders %tr diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml index e435ea30f00..36f4b636d22 100644 --- a/app/views/projects/_recent_commits.html.haml +++ b/app/views/projects/_recent_commits.html.haml @@ -6,7 +6,7 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p{:style => "margin-bottom: 3px;"} %strong - = link_to truncate_commit_message(commit, 60), project_commit_path(@project, :id => commit.id) + = link_to commit.truncated_message(60), project_commit_path(@project, :id => commit.id) %span %span.author diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml index 1af7fe3bb24..36f1435b15c 100644 --- a/app/views/projects/_recent_messages.html.haml +++ b/app/views/projects/_recent_messages.html.haml @@ -19,7 +19,7 @@ - css_class = "dash_commit" - commit = parent - item_code = commit.author.email - - link_item_name = truncate_commit_message(commit, 50) + - link_item_name = commit.truncated_message(50) - link_to_item = project_commit_path(@project, :id => commit.id) - else - css_class = "dash_wall" diff --git a/app/views/projects/_tree.html.haml b/app/views/projects/_tree.html.haml index d1903586400..11b04a3ba49 100644 --- a/app/views/projects/_tree.html.haml +++ b/app/views/projects/_tree.html.haml @@ -30,15 +30,15 @@ %th Last Update %th Last commit - = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right" + = link_to "history", project_commits_path(@project, :path => params[:path], :branch => params[:branch],:tag => params[:tag]), :class => "right" - if params[:path] - file = File.join(params[:path], "..") %tr{ :class => "tree-item", :url => tree_file_project_path(@project, @commit.id, file) } %td.tree-item-file-name = image_tag "dir.png" = link_to "..", tree_file_project_path(@project, @commit.id, file, :branch => @branch, :tag => @tag), :remote => :true - %td - %td + %td + %td - contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content| = render :partial => "projects/tree_item", :locals => { :content => content } @@ -52,7 +52,7 @@ }); - if params[:path] && request.xhr? - :javascript + :javascript $(window).unbind('popstate'); $(window).bind('popstate', function() { if(location.pathname.search("tree") != -1) { diff --git a/app/views/projects/_tree_file.html.haml b/app/views/projects/_tree_file.html.haml index 41a2287aa8c..b5b18213887 100644 --- a/app/views/projects/_tree_file.html.haml +++ b/app/views/projects/_tree_file.html.haml @@ -4,7 +4,7 @@ %strong = name = link_to "raw", blob_project_path(@project, :commit_id => @commit.id, :path => params[:path] ), :class => "right", :target => "_blank" - = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right", :style => "margin-right:10px;" + = link_to "history", project_commits_path(@project, :path => params[:path], :branch => params[:branch], :tag => params[:tag] ), :class => "right", :style => "margin-right:10px;" %br/ - if file.text? .view_file_content @@ -14,6 +14,6 @@ .view_file_content_image %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else - %p + %p %center No preview for this file type diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 4ebcfbefeab..538d2cc6e68 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -12,4 +12,4 @@ = time_ago_in_words(content_commit.committed_date) ago %td - = link_to truncate_commit_message(content_commit, 40), project_commit_path(@project, content_commit) + = link_to content_commit.truncated_message(40), project_commit_path(@project, content_commit) diff --git a/config/initializers/grit_ext.rb b/config/initializers/grit_ext.rb index 4bd71003532..9231da6ff9b 100644 --- a/config/initializers/grit_ext.rb +++ b/config/initializers/grit_ext.rb @@ -6,3 +6,7 @@ Grit::Blob.class_eval do include Utils::FileHelper include Utils::Colorize end + +Grit::Commit.class_eval do + include CommitExt +end diff --git a/lib/commit_ext.rb b/lib/commit_ext.rb new file mode 100644 index 00000000000..c175fa0cc2d --- /dev/null +++ b/lib/commit_ext.rb @@ -0,0 +1,9 @@ +module CommitExt + # Cause of encoding rails truncate raise error + # this method is temporary decision + def truncated_message(size = 80) + message.length > size ? (message[0..(size - 1)] + "...") : message + rescue + "-- invalid encoding for commit message" + end +end -- cgit v1.2.1 From 1d69788099f4c28c8b6795cacc9a1ffaa8600ee0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 25 Oct 2011 19:23:39 +0300 Subject: fix encoding error, issues critical status added --- app/assets/stylesheets/projects.css.scss | 34 ++++++++++++++++++++++ app/controllers/issues_controller.rb | 2 +- app/models/issue.rb | 11 +++++++ app/views/commits/_commits.html.haml | 2 +- app/views/commits/show.html.haml | 4 +-- app/views/issues/_form.html.haml | 12 +++++--- app/views/issues/_issues.html.haml | 7 ++++- app/views/issues/_show.html.haml | 20 +++++++++++-- app/views/projects/_recent_commits.html.haml | 2 +- app/views/projects/_recent_messages.html.haml | 2 +- app/views/projects/_tree_item.html.haml | 2 +- .../20111025134235_add_high_label_to_issue.rb | 5 ++++ db/schema.rb | 3 +- lib/commit_ext.rb | 10 ++++--- spec/requests/team_members_spec.rb | 4 ++- 15 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 db/migrate/20111025134235_add_high_label_to_issue.rb diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 8caa2810e78..3e7633a6ae2 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -647,3 +647,37 @@ tbody tr:nth-child(2n) td, tbody tr.even td { background: none repeat scroll 0 0 #FFBBBB } } + +.tag { + @include round-borders-all(4px); + padding:2px 4px; + border:none; + + &.high { + background: #D12F19; + color:white; + } + + &.today { + background: #44aa22; + color:white; + } + + &.yours { + background: #4466cc; + color:white; + } + &.notes { + background: #2c5c66; + color:white; + } +} + +#issues-table .issue { + &.critical { + td { + //background: #D12F19; + //color:#fff; + } + } +} diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index cf8e1ebe661..757ada94767 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -12,7 +12,7 @@ class IssuesController < ApplicationController def index @issues = case params[:f].to_i - when 1 then @project.issues.all + when 1 then @project.issues when 2 then @project.issues.closed when 3 then @project.issues.opened.assigned(current_user) else @project.issues.opened diff --git a/app/models/issue.rb b/app/models/issue.rb index 556cdc1c1c9..904965ac7e5 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -18,11 +18,22 @@ class Issue < ActiveRecord::Base :presence => true, :length => { :within => 0..2000 } + scope :critical, where(:critical => true) + scope :non_critical, where(:critical => false) + scope :opened, where(:closed => false) scope :closed, where(:closed => true) scope :assigned, lambda { |u| where(:assignee_id => u.id)} acts_as_list + + def today? + Date.today == created_at.to_date + end + + def new? + today? && created_at == updated_at + end end # == Schema Information # diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 3ed70f1cffb..9982306547e 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -11,7 +11,7 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p %strong - = commit.truncated_message + = truncate(commit.safe_message, :length => 60) = link_to "Browse Code", tree_project_path(@project, :commit_id => commit.id), :class => "lite_button", :style => "float:right" = link_to truncate(commit.id.to_s, :length => 16), project_commit_path(@project, :id => commit.id), :class => "lite_button", :style => "width:120px;float:right" %span diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 0ba42618470..3beeada8c8c 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -1,5 +1,5 @@ %h3 - = "[ #{@commit.committer} ] #{@commit.truncated_message(40)}" + = "[ #{@commit.committer} ] #{truncate(@commit.safe_message)}" -#= link_to 'Back', project_commits_path(@project), :class => "button" %table.round-borders %tr @@ -16,7 +16,7 @@ %td= @commit.committed_date %tr %td Message - %td= @commit.message + %td= @commit.safe_message %tr %td Tree %td= link_to 'Browse Code', tree_project_path(@project, :commit_id => @commit.id) diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index 71acdba1895..eae0ee0f52c 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -5,17 +5,21 @@ - @issue.errors.full_messages.each do |msg| %li= msg - .span-6 + .span-8 = f.label :title = f.text_field :title, :style => "width:450px" - .span-6 + .span-8 = f.label :content = f.text_area :content, :style => "width:450px; height:130px" - .span-6.append-bottom + .span-8.append-bottom = f.label :assignee_id = f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }) + .span-1 + = f.label :critical, "Critical" + %br + = f.check_box :critical - unless @issue.new_record? - .span-3.right + .span-2.right = f.label :closed %br = f.check_box :closed diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml index d3e21940346..42e8371a0ba 100644 --- a/app/views/issues/_issues.html.haml +++ b/app/views/issues/_issues.html.haml @@ -1,10 +1,15 @@ %table.round-borders#issues-table %tr + - if can?(current_user, :admin_issue, @project) && !params[:f] || params[:f] == "0" + %th %th Assignee %th ID %th Title %th Closed? %th - - @issues.each do |issue| + - @issues.critical.each do |issue| + = render(:partial => 'show', :locals => {:issue => issue}) + + - @issues.non_critical.each do |issue| = render(:partial => 'show', :locals => {:issue => issue}) diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml index 30157257ff5..fcac006e81c 100644 --- a/app/views/issues/_show.html.haml +++ b/app/views/issues/_show.html.haml @@ -1,10 +1,24 @@ -%tr{ :id => dom_id(issue), :class => "issue", :url => project_issue_path(@project, issue) } +%tr{ :id => dom_id(issue), :class => "issue #{issue.critical ? "critical" : ""}", :url => project_issue_path(@project, issue) } + - if can?(current_user, :admin_issue, @project) && !params[:f] || params[:f] == "0" + %td + = image_tag "move.png" , :class => [:handle, :left] %td - = image_tag "move.png" , :class => [:handle, :left] = image_tag gravatar_icon(issue.assignee.email), :class => "left", :width => 40, :style => "padding:0 5px;" = truncate issue.assignee.name, :lenght => 20 %td ##{issue.id} - %td= html_escape issue.title + %td + = html_escape issue.title + %br + - if issue.critical + %span.tag.high critical + - if issue.today? + %span.tag.today today + -#- if issue.author == current_user + -#%span.tag.yours yours + -#- if issue.notes.count > 0 + -#%span.tag.notes + -#= issue.notes.count + -#notes %td - if can? current_user, :write_issue, @project = form_for([@project, issue], :remote => true) do |f| diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml index 36f4b636d22..3157c356862 100644 --- a/app/views/projects/_recent_commits.html.haml +++ b/app/views/projects/_recent_commits.html.haml @@ -6,7 +6,7 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p{:style => "margin-bottom: 3px;"} %strong - = link_to commit.truncated_message(60), project_commit_path(@project, :id => commit.id) + = link_to truncate(commit.safe_message, :length => 60), project_commit_path(@project, :id => commit.id) %span %span.author diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml index 36f1435b15c..67f3449bd9b 100644 --- a/app/views/projects/_recent_messages.html.haml +++ b/app/views/projects/_recent_messages.html.haml @@ -19,7 +19,7 @@ - css_class = "dash_commit" - commit = parent - item_code = commit.author.email - - link_item_name = commit.truncated_message(50) + - link_item_name = truncate(commit.safe_message, :length => 50) - link_to_item = project_commit_path(@project, :id => commit.id) - else - css_class = "dash_wall" diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 538d2cc6e68..53c05d5a429 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -12,4 +12,4 @@ = time_ago_in_words(content_commit.committed_date) ago %td - = link_to content_commit.truncated_message(40), project_commit_path(@project, content_commit) + = link_to truncate(content_commit.safe_message, :length => 40), project_commit_path(@project, content_commit) diff --git a/db/migrate/20111025134235_add_high_label_to_issue.rb b/db/migrate/20111025134235_add_high_label_to_issue.rb new file mode 100644 index 00000000000..676eaaf8c1b --- /dev/null +++ b/db/migrate/20111025134235_add_high_label_to_issue.rb @@ -0,0 +1,5 @@ +class AddHighLabelToIssue < ActiveRecord::Migration + def change + add_column :issues, :critical, :boolean, :default => false, :null => false + end +end diff --git a/db/schema.rb b/db/schema.rb index cc805990a8b..21c224d4a90 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111021101550) do +ActiveRecord::Schema.define(:version => 20111025134235) do create_table "issues", :force => true do |t| t.string "title" @@ -23,6 +23,7 @@ ActiveRecord::Schema.define(:version => 20111021101550) do t.datetime "updated_at" t.boolean "closed", :default => false, :null => false t.integer "position", :default => 0 + t.boolean "critical", :default => false, :null => false end create_table "keys", :force => true do |t| diff --git a/lib/commit_ext.rb b/lib/commit_ext.rb index c175fa0cc2d..e09dbdaf8ef 100644 --- a/lib/commit_ext.rb +++ b/lib/commit_ext.rb @@ -1,8 +1,10 @@ module CommitExt - # Cause of encoding rails truncate raise error - # this method is temporary decision - def truncated_message(size = 80) - message.length > size ? (message[0..(size - 1)] + "...") : message + def safe_message + message.encode("UTF-8", + :invalid => :replace, + :undef => :replace, + :universal_newline => true, + :replace => "") rescue "-- invalid encoding for commit message" end diff --git a/spec/requests/team_members_spec.rb b/spec/requests/team_members_spec.rb index ec0af3214a9..650ed4497f2 100644 --- a/spec/requests/team_members_spec.rb +++ b/spec/requests/team_members_spec.rb @@ -10,7 +10,9 @@ describe "TeamMembers" do describe "View profile" do it "should be available" do visit(team_project_path(@project)) - find(:xpath, "//table[@id='team-table']//a[1]").click + within "#team-table" do + click_link(@user.name) + end page.should have_content @user.skype page.should_not have_content 'Twitter' end -- cgit v1.2.1 From 23187d60c4eb3eb2d51459ab18fd16da9caa5b58 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 25 Oct 2011 21:07:39 +0300 Subject: fixed diff issue --- app/views/commits/_text_file.html.haml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/views/commits/_text_file.html.haml b/app/views/commits/_text_file.html.haml index b20aa8afe40..67033adea7e 100644 --- a/app/views/commits/_text_file.html.haml +++ b/app/views/commits/_text_file.html.haml @@ -1,8 +1,14 @@ +- line_old = 0 +- line_new = 0 - lines_arr = diff.diff.lines.to_a -- line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 -- line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 -- lines = lines_arr[3..-1].join -- lines.each_line do |line| +- lines_arr.each do |line| + - next if line.match(/^--- a/) + - next if line.match(/^\+\+\+ b/) + - if line.match(/^@@ -/) + - line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 + - line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 + - next + = diff_line(line, line_new, line_old) - if line[0] == "+" - line_new += 1 -- cgit v1.2.1 From fb40d27d1e8be0b516e3cf101995b54052872f97 Mon Sep 17 00:00:00 2001 From: VSizov Date: Fri, 28 Oct 2011 11:34:30 +0300 Subject: remove spaces --- app/controllers/projects_controller.rb | 2 +- app/views/projects/empty.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 54d19af7511..c5c05e6f3d6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -115,7 +115,7 @@ class ProjectsController < ApplicationController respond_to do |format| format.html # show.html.erb format.js do - # diasbale cache to allow back button works + # disable cache to allow back button works response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" response.headers["Pragma"] = "no-cache" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" diff --git a/app/views/projects/empty.html.erb b/app/views/projects/empty.html.erb index 4c60facde8c..c84b17ff8c7 100644 --- a/app/views/projects/empty.html.erb +++ b/app/views/projects/empty.html.erb @@ -28,7 +28,7 @@ eos

    Existing Git Repo?

    <% exist_repo_setup_str = < -- cgit v1.2.1 From 121f6d04faedad570e6ce1d8d7e235b55c2a9048 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sun, 6 Nov 2011 21:12:23 +0200 Subject: fixed conflicts --- app/assets/images/ajax-loader.gif | Bin 6820 -> 4178 bytes public/favicon.ico | Bin 0 -> 1150 bytes 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 public/favicon.ico diff --git a/app/assets/images/ajax-loader.gif b/app/assets/images/ajax-loader.gif index 0ca7ada9605..c97ec6ea973 100644 Binary files a/app/assets/images/ajax-loader.gif and b/app/assets/images/ajax-loader.gif differ diff --git a/public/favicon.ico b/public/favicon.ico old mode 100644 new mode 100755 index e69de29bb2d..4ec0d298859 Binary files a/public/favicon.ico and b/public/favicon.ico differ -- cgit v1.2.1 From 1d30877a68291ebd46fd995398394be6e7724141 Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 14:29:09 +0100 Subject: Moved shoulda to the :test group in Gemfile. This prevennts `rake test` from running after every other rake command. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f32c13f070f..806280fdb03 100644 --- a/Gemfile +++ b/Gemfile @@ -37,7 +37,6 @@ end group :development, :test do gem 'rspec-rails' - gem "shoulda", "~> 3.0.0.beta2" gem 'capybara' gem 'autotest' gem 'autotest-rails' @@ -51,4 +50,5 @@ end group :test do gem 'turn', :require => false gem 'simplecov', :require => false + gem "shoulda", "~> 3.0.0.beta2" end -- cgit v1.2.1 From 4dbed7ca88c2fe8f127deb752f82a5ee9ab4cade Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 15:15:21 +0100 Subject: Use secure.gravatar.com when running over SSL --- app/helpers/application_helper.rb | 4 +++- spec/helpers/application_helper_spec.rb | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 spec/helpers/application_helper_spec.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 70047c41014..a49d3dea217 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,7 +1,9 @@ require 'digest/md5' module ApplicationHelper + def gravatar_icon(user_email) - "http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon" + gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com" + "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon" end def fixed_mode? diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb new file mode 100644 index 00000000000..3e174ca47ab --- /dev/null +++ b/spec/helpers/application_helper_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe ApplicationHelper do + context ".gravatar_icon" do + context "over http" do + it "returns the correct URL to www.gravatar.com" do + expected = "http://www.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" + + # Pretend we're running over HTTP + helper.stub(:request) do + request = double('request') + request.stub(:ssl?) { false } + request + end + + helper.gravatar_icon("admin@local.host").should == expected + end + end + + context "over https" do + it "returns the correct URL to secure.gravatar.com" do + expected = "https://secure.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" + + # Pretend we're running over HTTPS + helper.stub(:request) do + request = double('request') + request.stub(:ssl?) { true } + request + end + + helper.gravatar_icon("admin@local.host").should == expected + end + end + end +end -- cgit v1.2.1 From 4454b6c593e51c5f23a7d391eb6a1f46aaa6bb45 Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 16:59:51 +0100 Subject: Show commits as 'Commit' instead of 'Grit::Commit' --- app/helpers/dashboard_helper.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 0560af5daf5..8921ee085dc 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -19,12 +19,15 @@ module DashboardHelper end def dashboard_feed_title(object) - title = case object.class.name.to_s + klass = object.class.to_s.split("::").last + + title = case klass when "Note" then markdown(object.note) when "Issue" then object.title - when "Grit::Commit" then object.safe_message + when "Commit" then object.safe_message else "" end - "[#{object.class.name}] #{truncate(sanitize(title, :tags => []), :length => 60)} " + + "[#{klass}] #{truncate(sanitize(title, :tags => []), :length => 60)} " end end -- cgit v1.2.1 From 786fe29db4cd884ad0735d7ac2455975aff11ae8 Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 14:29:09 +0100 Subject: Moved shoulda to the :test group in Gemfile. This prevennts `rake test` from running after every other rake command. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f32c13f070f..806280fdb03 100644 --- a/Gemfile +++ b/Gemfile @@ -37,7 +37,6 @@ end group :development, :test do gem 'rspec-rails' - gem "shoulda", "~> 3.0.0.beta2" gem 'capybara' gem 'autotest' gem 'autotest-rails' @@ -51,4 +50,5 @@ end group :test do gem 'turn', :require => false gem 'simplecov', :require => false + gem "shoulda", "~> 3.0.0.beta2" end -- cgit v1.2.1 From 85468fb4ce47633a5996104c87d03eaa2cc0031e Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 15:15:21 +0100 Subject: Use secure.gravatar.com when running over SSL --- app/helpers/application_helper.rb | 4 +++- spec/helpers/application_helper_spec.rb | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 spec/helpers/application_helper_spec.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 70047c41014..a49d3dea217 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,7 +1,9 @@ require 'digest/md5' module ApplicationHelper + def gravatar_icon(user_email) - "http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon" + gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com" + "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon" end def fixed_mode? diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb new file mode 100644 index 00000000000..3e174ca47ab --- /dev/null +++ b/spec/helpers/application_helper_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe ApplicationHelper do + context ".gravatar_icon" do + context "over http" do + it "returns the correct URL to www.gravatar.com" do + expected = "http://www.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" + + # Pretend we're running over HTTP + helper.stub(:request) do + request = double('request') + request.stub(:ssl?) { false } + request + end + + helper.gravatar_icon("admin@local.host").should == expected + end + end + + context "over https" do + it "returns the correct URL to secure.gravatar.com" do + expected = "https://secure.gravatar.com/avatar/f7daa65b2aa96290bb47c4d68d11fe6a?s=40&d=identicon" + + # Pretend we're running over HTTPS + helper.stub(:request) do + request = double('request') + request.stub(:ssl?) { true } + request + end + + helper.gravatar_icon("admin@local.host").should == expected + end + end + end +end -- cgit v1.2.1 From e608eacc4bbbaf7c2b3bd81d3ef8a24da3df03be Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Wed, 9 Nov 2011 16:59:51 +0100 Subject: Show commits as 'Commit' instead of 'Grit::Commit' Conflicts: app/helpers/dashboard_helper.rb --- app/helpers/dashboard_helper.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 03f6a015886..422097d1d2b 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -19,12 +19,15 @@ module DashboardHelper end def dashboard_feed_title(object) - title = case object.class.name.to_s + klass = object.class.to_s.split("::").last + + title = case klass when "Note" then markdown(object.note) when "Issue" then object.title - when "Grit::Commit" then object.safe_message + when "Commit" then object.safe_message else return "Project Wall" end - "[#{object.class.name}] #{truncate(sanitize(title, :tags => []), :length => 60)} " + + "[#{klass}] #{truncate(sanitize(title, :tags => []), :length => 60)} " end end -- cgit v1.2.1 From eb96336de1f8fa44d8c74567ce35c9f8a7069d23 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Nov 2011 21:26:24 +0200 Subject: 1. Set Helvetica as primary font; 2. removed tabs --- app/assets/stylesheets/style.scss | 10 ++++++++++ app/helpers/issues_helper.rb | 20 ++++++++++---------- app/helpers/tags_helper.rb | 21 ++++++++++----------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index 13f26d97d44..b6bc354e35c 100755 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -670,3 +670,13 @@ body.projects-page .browse-code{margin-right: 10px} p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } } + +/** + * author:DZ + * date: Nov 09 + * fix different fonts for firefox & webkit + */ +body, button, input, select, textarea { + font-family: "Helvetica", sans-serif; +} + diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index bdb6c281870..d09fad5f33c 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -1,12 +1,12 @@ module IssuesHelper - def sort_class - if can?(current_user, :admin_issue, @project) && (!params[:f] || params[:f] == "0") - "handle" - end - end - - def project_issues_filter_path project, params = {} - params[:f] ||= cookies['issue_filter'] - project_issues_path project, params - end + def sort_class + if can?(current_user, :admin_issue, @project) && (!params[:f] || params[:f] == "0") + "handle" + end + end + + def project_issues_filter_path project, params = {} + params[:f] ||= cookies['issue_filter'] + project_issues_path project, params + end end diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index 5c1ecc8689b..ebed6a83746 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -1,15 +1,14 @@ module TagsHelper - def tag_path tag - "/tags/#{tag}" - end + def tag_path tag + "/tags/#{tag}" + end - def tag_list project - html = '' - project.tag_list.each do |tag| - html += link_to tag, tag_path(tag) - end - - html.html_safe - end + def tag_list project + html = '' + project.tag_list.each do |tag| + html += link_to tag, tag_path(tag) + end + html.html_safe + end end -- cgit v1.2.1 From 9a5f328313a85142af669b6745825e3e2c7b3005 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Nov 2011 09:46:04 +0200 Subject: notes should depends on user when destroy --- app/models/user.rb | 4 ++++ spec/models/user_spec.rb | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index c90cf49f636..1d41028d59a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -16,6 +16,10 @@ class User < ActiveRecord::Base :foreign_key => :author_id, :dependent => :destroy + has_many :notes, + :foreign_key => :author_id, + :dependent => :destroy + has_many :assigned_issues, :class_name => "Issue", :foreign_key => :assignee_id, diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d66b1c251f0..14d48114c30 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -18,6 +18,21 @@ describe User do user = User.new(:email => "test@mail.com") user.identifier.should == "test_mail.com" end + + describe "dependent" do + before do + @user = Factory :user + @note = Factory :note, + :author => @user, + :project => Factory(:project) + end + + it "should destroy all notes with user" do + Note.find_by_id(@note.id).should_not be_nil + @user.destroy + Note.find_by_id(@note.id).should be_nil + end + end end # == Schema Information # -- cgit v1.2.1 From f7756b8404046fc0d0db2aa5e2b05c17cf16fba8 Mon Sep 17 00:00:00 2001 From: Saito Date: Thu, 10 Nov 2011 16:24:11 +0800 Subject: chmod -x style.scss --- app/assets/stylesheets/style.scss | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/assets/stylesheets/style.scss diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss old mode 100755 new mode 100644 -- cgit v1.2.1 From ff0dc87e3d9db87dc9a8bbc24ed3d9bca10b2590 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 18:15:47 -0300 Subject: fixes css --- app/assets/stylesheets/projects.css.scss | 2 +- app/assets/stylesheets/style.scss | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 10885d3bd0a..ea74f28cf58 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -551,7 +551,7 @@ pre.commit_message { } #container { - min-height:100%; +/* min-height:100%;*/ } .ui-selectmenu{ @include round-borders-all(4px); diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index b6bc354e35c..e82e030a073 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -302,7 +302,7 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%} /* eo Icons*/ /* General */ -#container{background-color: white; overflow: hidden;} +#container{background-color: white; overflow: hidden; } body.collapsed #container{margin: auto; width: 980px; border: 1px solid rgba(0,0,0,.22); border-top: 0; box-shadow: 0 0 0px 4px rgba(0,0,0,.04)} @@ -506,9 +506,9 @@ body.dashboard-page header{margin-bottom: 0} body.dashboard-page .news-feed{padding-left: 1em; margin-right: 450px; margin-left: 1%} body.dashboard-page .dashboard-content{ position: relative; float: left; width: 100%; height: 100%; } body.dashboard-page .news-feed h2{float: left;} -body.dashboard-page aside{ width: 420px; float: right; right: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; border-left: 1px solid #ccc } +body.dashboard-page aside{ min-height: 700px; width: 420px; float: right; background-color: #f7f7f7; border-left: 1px solid #ccc } body.dashboard-page aside h4{margin: 0; border-bottom: 1px solid #ccc; padding: 10px 10px; font-size: 11px; font-weight: bold; text-transform: uppercase;} -body.dashboard-page aside h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 4%; margin-top: -4px; display: block;} +body.dashboard-page aside h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 2%; margin-top: -4px; display: block;} body.dashboard-page aside .project-list {list-style: none; margin: 0; padding: 0;} body.dashboard-page aside .project-list li a {background: white; color: #{$blue_link}; display: block; border-bottom: 1px solid #eee; padding: 14px 6% 14px 14px;} body.dashboard-page aside .project-list li a:hover {background: #f1f1f1} -- cgit v1.2.1 From e56420aebf4db32cdf878f96e9ba94587156f3bd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Nov 2011 23:40:46 +0200 Subject: preparing to 1.2 --- CHANGELOG | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 96acfba1440..7a438351e07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +v 1.2.0 + - new design + - user dashboard + - markdown support for comments + - encoding issues + - wall like twitter timeline + v 1.1.0 - project dashboard - wall redesigned diff --git a/VERSION b/VERSION index 9084fa2f716..26aaba0e866 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.2.0 -- cgit v1.2.1 From 56e449df50089d972a6af3c59f10c78d2ca0902a Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 18:55:55 -0300 Subject: fixing account box dropdown --- app/assets/javascripts/application.js | 12 ++++++++++++ app/assets/stylesheets/style.scss | 36 ++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 310463791d8..4196f8a44db 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -19,8 +19,20 @@ $(function(){ $('select#branch').selectmenu({style:'popup', width:200}); $('select#tag').selectmenu({style:'popup', width:200}); + + $(".account-box").mouseenter(showMenu); + $(".account-box").mouseleave(resetMenu); + }); function updatePage(data){ $.ajax({type: "GET", url: location.href, data: data, dataType: "script"}); } + +function showMenu() { + $(this).toggleClass('hover'); +} + +function resetMenu() { + $(this).removeClass("hover"); +} \ No newline at end of file diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index e82e030a073..f9e7c90af3b 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -144,7 +144,8 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF} background-image: -o-linear-gradient(#f7f7f7 7.6%, #d5d5d5); } -.button{ +a.button, input.button{ + font-weight: bold; padding: 10px 20px; text-align: center; display: inline-block; @@ -159,6 +160,8 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF} background-image: -o-linear-gradient(#dbf5f6 79.4%, #c5eef0); } +input.button{margin-bottom: 1.5em} + .button:hover {color: rgba(0,0,0,.8)} .button.green {margin-right: 0; } @@ -308,7 +311,7 @@ body.collapsed #container{margin: auto; width: 980px; border: 1px solid rgba(0,0 /* Header */ -header{background: #474D57 url('bg-header.png') repeat-x bottom; z-index: 10000; height: 44px; padding: 10px 2% 6px 2%} +header{background: #474D57 url('bg-header.png') repeat-x bottom; z-index: 10000; height: 44px; padding: 10px 2% 6px 2%; position: relative} header a{color: white; text-shadow: 0 -1px 0 black} header a:hover{color: #f1f1f1} header h1{ @@ -370,7 +373,7 @@ header nav a.admin{ } -header .search{ display: inline-block; float: right; margin-right: 10px} +header .search{ display: inline-block; float: right; margin-right: 46px} header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;} @@ -426,10 +429,11 @@ header{margin-bottom: 0; clear: both; } .page-title a.grey-button{float: right;} .right{float: right;} + /* Account box */ -header .account-box{position: relative;z-index: 10000; top: -3px; width: 38px; height: 38px; font-size: 11px; float: right; display: block; cursor: pointer; } -header .account-box img{ border-radius: 4px; width: 38px; height: 38px; display: block; box-shadow: 0 1px 2px black} -header .account-box:after{ +header .account-box{position: absolute; right: 0; top: 8px; z-index: 10000; width: 128px; height: 138px; font-size: 11px; float: right; display: block; cursor: pointer;} +header .account-box img{ border-radius: 4px; right: 20px; position: absolute; width: 38px; height: 38px; display: block; box-shadow: 0 1px 2px black} +header .account-box img:after{ content: " "; display: block; position: absolute; @@ -449,11 +453,21 @@ float: right; background-origin: border-box; } -.account-box:hover > .account-links, .account-box:hover > .arrow-up{display: block;} -header .account-links{background: white; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); } -header .account-links a{color: #666; padding: 6px 10px; display: block; text-shadow: none; border-bottom: 1px solid #eee} -header .account-links a:hover{background-color: #f1f1f1; text-shadow: none; color: #333} +.account-box.hover{} +.account-box.hover .account-links, .account-box.hover .arrow-up{display: block;} +/* Inspired by http://maxvoltar.com/temp/nowplaying/ */ +header .account-links{background: white; display: none; z-index: 100000; border-radius: 5px; width: 100px; position: absolute; right: 20px; top: 46px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); } +header .account-links a{color: #666; padding: 6px 10px; display: block; text-shadow: none; border-bottom: 1px solid #eee} +header .account-links a:hover{ + background: #3aacec; + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#39acec), to(#279ada), color-stop(.05, #4cbefe)); + background: -moz-linear-gradient(top, #39acec, #4cbefe 5%, #279ada); + background: linear-gradient(top, #39acec, #4cbefe 5%, #279ada); + color: #fff; + text-shadow: #1488c8 0 -1px 0; +} +.account-box.hover .arrow-up{top: 41px; right: 6px; position: absolute} header .account-links a:first-child{ -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; @@ -548,7 +562,7 @@ body.project-page h2.icon .project-name i.arrow{float: right; body.project-page h2.icon span{ background-position: -78px -68px; } body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; } body.project-page .page-title{margin-bottom: 0} -body.project-page .project-sidebar {width: 220px; left: 0; top: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; border-left: 1px solid #ccc; float: left; display: inline-block; background: #f7f7f7; padding: 20px 0 20px 2%; margin: 0; } +body.project-page .project-sidebar {width: 220px; left: 0; top: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; float: left; display: inline-block; background: #f7f7f7; padding: 20px 0 20px 2%; margin: 0; } body.project-page .project-sidebar input.text.git-url{ font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 14px 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px;} body.project-page .project-sidebar aside{width: 219px} body.project-page .project-sidebar aside a{display: block; position: relative; background: white; padding: 15px 10px; border-bottom: 1px solid #eee} -- cgit v1.2.1 From 1155f2d1d221be3a04b9eeba9037617915efcdd4 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 18:57:47 -0300 Subject: fix css --- app/assets/stylesheets/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index f9e7c90af3b..65bdb40af83 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -431,7 +431,7 @@ header{margin-bottom: 0; clear: both; } /* Account box */ -header .account-box{position: absolute; right: 0; top: 8px; z-index: 10000; width: 128px; height: 138px; font-size: 11px; float: right; display: block; cursor: pointer;} +header .account-box{position: absolute; right: 0; top: 8px; z-index: 10000; width: 128px; font-size: 11px; float: right; display: block; cursor: pointer;} header .account-box img{ border-radius: 4px; right: 20px; position: absolute; width: 38px; height: 38px; display: block; box-shadow: 0 1px 2px black} header .account-box img:after{ content: " "; @@ -453,7 +453,7 @@ float: right; background-origin: border-box; } -.account-box.hover{} +.account-box.hover{height: 138px;} .account-box.hover .account-links, .account-box.hover .arrow-up{display: block;} /* Inspired by http://maxvoltar.com/temp/nowplaying/ */ -- cgit v1.2.1 From 4ecf17cf9a81c6576244cd1bd8f1751ac30dc296 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Nov 2011 00:08:20 +0200 Subject: annotated --- app/models/issue.rb | 1 + app/models/note.rb | 2 +- spec/models/issue_spec.rb | 1 + spec/models/note_spec.rb | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index f649cacbacf..19b174d86b7 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -54,5 +54,6 @@ end # updated_at :datetime # closed :boolean default(FALSE), not null # position :integer default(0) +# critical :boolean default(FALSE), not null # diff --git a/app/models/note.rb b/app/models/note.rb index 3c59efefb1b..c3c10639bf9 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -38,7 +38,7 @@ end # Table name: notes # # id :integer not null, primary key -# note :string(255) +# note :text # noteable_id :string(255) # noteable_type :string(255) # author_id :integer diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index e0559ab6663..da2d88f67f8 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -39,5 +39,6 @@ end # updated_at :datetime # closed :boolean default(FALSE), not null # position :integer default(0) +# critical :boolean default(FALSE), not null # diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 5b16ad3682c..038e4f9d36e 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -66,7 +66,7 @@ end # Table name: notes # # id :integer not null, primary key -# note :string(255) +# note :text # noteable_id :string(255) # noteable_type :string(255) # author_id :integer -- cgit v1.2.1 From 60a9c051db3dc8dfe968932a6163830a6afa142f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Nov 2011 00:22:04 +0200 Subject: removed br tag --- app/views/dashboard/index.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index 8d48d1f70b5..e7559dd5762 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -33,6 +33,5 @@ authored = time_ago_in_words(update.created_at) ago - %br / #news-feed / #dashboard-content -- cgit v1.2.1 From 88466881b8eb2499dd3758e85f261b3281095fd6 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 19:24:44 -0300 Subject: new layout for add project --- app/assets/stylesheets/style.scss | 7 ++++++- app/views/projects/new.html.haml | 16 +++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index 65bdb40af83..bee69740078 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -144,7 +144,7 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF} background-image: -o-linear-gradient(#f7f7f7 7.6%, #d5d5d5); } -a.button, input.button{ +a.button, input.button, .lbutton { font-weight: bold; padding: 10px 20px; text-align: center; @@ -586,6 +586,11 @@ body.project-page table .commit a{color: #{$blue_link}} body.project-page table th, body.project-page table td{ border-bottom: 1px solid #DEE2E3;} body.project-page .fixed{position: fixed; } +/* New project Page */ +.new-project-page .container{width: 600px; background-color: rgba(0,0,0,.02); margin: auto; border: 1px solid #eee; padding: 0 20px; margin: 30px auto 60px auto; border-radius: 5px} +.new-project-page .container table{background: white} +/* eo New Project Page */ + /* Commit Page */ body.project-page.commits-page .commit-info{float: right;} body.project-page.commits-page .commit-info data{ diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 65639a53649..1f8d250a200 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,8 +1,10 @@ -.new-project-hodler - .container_4 - %h2.icon - %span - New Project +- content_for(:body_class, "new-project-page") +- content_for(:page_title) do + .new-project-hodler + .container + %h2.icon + %span + New Project - %div.clear - = render 'form' + %div.clear + = render 'form' -- cgit v1.2.1 From 99bd00158c49360cf6193118772396d19111d9a0 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 19:42:34 -0300 Subject: fixing errors style --- app/assets/stylesheets/projects.css.scss | 9 +------ app/assets/stylesheets/style.scss | 44 ++++++++++++-------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index ea74f28cf58..29dc267407e 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -460,14 +460,7 @@ input.ssh_project_url { } } -.field_with_errors { - input[type="text"], - input[type="password"], - textarea - { - background: none repeat scroll 0 0 #FFBBBB - } -} + .tag { @include round-borders-all(4px); diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index bee69740078..9e093fd4314 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -304,6 +304,22 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%} /* eo Icons*/ +/* Errors */ +#error_explanation{background: #ffe5eb; padding: 20px; margin-bottom: 20px; border-radius: 5px} +#error_explanation h2{margin: 0; margin-bottom: 20px; color: red} +#error_explanation ul li{margin-bottom: 10px} +#error_explanation ul li:last-child{margin-bottom: 0} +.field_with_errors { + input[type="text"], + input[type="password"], + textarea + { + border: 1px solid #FFBBBB; + background: #fff4f6; + } +} +/* eo Errors */ + /* General */ #container{background-color: white; overflow: hidden; } body.collapsed #container{margin: auto; width: 980px; border: 1px solid rgba(0,0,0,.22); border-top: 0; box-shadow: 0 0 0px 4px rgba(0,0,0,.04)} @@ -380,33 +396,7 @@ header nav a span{width: 20px; height: 20px; display: inline-block; background: header nav a.dashboard span{background: url('images.png') no-repeat -161px 0;} header nav a.admin span{background: url('images.png') no-repeat -184px 0;} header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 7px} -/* - -header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 14px;} - -header nav a.dashboard.current span{ background-position: -163px -22px; } -header nav a.admin.current span{ background-position: -186px -22px;} -header nav a.project.current span{ background-position: -211px -23px;} - -header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 15px} - - -header nav a span.current{top: 18px} - - -header nav {margin-left: 180px; display: inline-block; float: left;} -header nav a{float: left; background: #31363e; padding: 16px 20px 20px 34px; margin-right: 10px; - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topleft: 5px; - -moz-border-radius-topright: 5px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - position: relative; -} -header nav a.current{background: white; color: #333; text-shadow: none;} -*/ header .login-top{float: right; width: 180px; background-image: -webkit-gradient(linear, 0 0, 0 62, color-stop(0.032, #464c56), to(#363c45)); @@ -579,7 +569,7 @@ body.project-page .project-sidebar aside a:first-child{ body.project-page .project-sidebar aside a:hover{background-color: #eee;} body.project-page .project-sidebar aside a span.number{float: right; border-radius: 5px; text-shadow: none; background: rgba(0,0,0,.12); text-align: center; padding: 5px 8px; position: absolute; top: 10px; right: 10px} body.project-page .project-sidebar aside a.current{background-color: #79c3e0; color: white; text-shadow: none; border-color: transparent} -body.project-page .project-content{ padding: 20px; display: block; margin-left: 250px } +body.project-page .project-content{ padding: 20px; display: block; margin-left: 250px; min-height: 400px} body.project-page .project-content h2{ margin-top: 6px} body.project-page .project-content .button.right{margin-left: 20px} body.project-page table .commit a{color: #{$blue_link}} -- cgit v1.2.1 From dab072c1ab609917c3472ae663f1db49af75e56e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Nov 2011 00:51:19 +0200 Subject: refactor code: repository.rb --- app/controllers/application_controller.rb | 2 +- app/controllers/projects_controller.rb | 2 +- app/models/project.rb | 69 +++++++------------------------ app/models/repository.rb | 65 +++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 56 deletions(-) create mode 100644 app/models/repository.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 782612c0aed..f16f1a39f6d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -64,7 +64,7 @@ class ApplicationController < ActionController::Base else @branch = params[:branch].blank? ? nil : params[:branch] @tag = params[:tag].blank? ? nil : params[:tag] - @ref = @branch || @tag || "master" + @ref = @branch || @tag || Repository.default_ref end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 2df5e9ccc27..5e00fe461e6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -108,7 +108,7 @@ class ProjectsController < ApplicationController if params[:commit_id] @commit = @repo.commits(params[:commit_id]).first else - @commit = @repo.commits(@ref || "master").first + @commit = @repo.commits(@ref).first end @tree = @commit.tree diff --git a/app/models/project.rb b/app/models/project.rb index a5b6224bd0e..9767352f1ee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -46,6 +46,21 @@ class Project < ActiveRecord::Base scope :public_only, where(:private_flag => false) + def repository + @repository ||= Repository.new(self) + end + + delegate :repo, + :tags, + :repo_exists?, + :commit, + :commits, + :tree, + :heads, + :commits_since, + :fresh_commits, + :to => :repository, :prefix => nil + def to_param code end @@ -114,18 +129,6 @@ class Project < ActiveRecord::Base GITOSIS["base_path"] + path + ".git" end - def repo - @repo ||= Grit::Repo.new(path_to_repo) - end - - def tags - repo.tags.map(&:name).sort.reverse - end - - def repo_exists? - repo rescue false - end - def last_activity updates(1).first rescue @@ -146,48 +149,6 @@ class Project < ActiveRecord::Base end[0...n] end - def commit(commit_id = nil) - if commit_id - repo.commits(commit_id).first - else - repo.commits.first - end - end - - def heads - @heads ||= repo.heads - end - - def fresh_commits(n = 10) - commits = heads.map do |h| - repo.commits(h.name, n) - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits[0...n] - end - - def commits_since(date) - commits = heads.map do |h| - repo.log(h.name, nil, :since => date) - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits - end - - def tree(fcommit, path = nil) - fcommit = commit if fcommit == :head - tree = fcommit.tree - path ? (tree / path) : tree - end - def check_limit unless owner.can_create_project? errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") diff --git a/app/models/repository.rb b/app/models/repository.rb new file mode 100644 index 00000000000..da679d8bb9e --- /dev/null +++ b/app/models/repository.rb @@ -0,0 +1,65 @@ +class Repository + attr_accessor :project + + def self.default_ref + "master" + end + + def initialize(project) + @project = project + end + + def repo + @repo ||= Grit::Repo.new(project.path_to_repo) + end + + def tags + repo.tags.map(&:name).sort.reverse + end + + def repo_exists? + repo rescue false + end + + def commit(commit_id = nil) + if commit_id + repo.commits(commit_id).first + else + repo.commits.first + end + end + + def tree(fcommit, path = nil) + fcommit = commit if fcommit == :head + tree = fcommit.tree + path ? (tree / path) : tree + end + + def fresh_commits(n = 10) + commits = heads.map do |h| + repo.commits(h.name, n) + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0...n] + end + + def heads + @heads ||= repo.heads + end + + def commits_since(date) + commits = heads.map do |h| + repo.log(h.name, nil, :since => date) + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits + end +end -- cgit v1.2.1 From d730e3ef8b1c9bf73f9eeb492968b455fbec319d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Nov 2011 01:28:26 +0200 Subject: refactoring project, commits controllers --- app/controllers/commits_controller.rb | 18 ++++++------ app/controllers/projects_controller.rb | 14 ++++----- app/models/project.rb | 24 ++++++---------- app/models/repository.rb | 52 ++++++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index 79703cf104a..67384cf5bea 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -8,18 +8,18 @@ class CommitsController < ApplicationController before_filter :add_project_abilities before_filter :authorize_read_project! before_filter :require_non_empty_project + before_filter :load_refs, :only => :index # load @branch, @tag & @ref - def index - load_refs # load @branch, @tag & @ref + def index @repo = project.repo limit, offset = (params[:limit] || 20), (params[:offset] || 0) - if params[:path] - @commits = @repo.log(@ref, params[:path], :max_count => limit, :skip => offset) - else - @commits = @repo.commits(@ref, limit, offset) - end + @commits = if params[:path] + @repo.log(@ref, params[:path], :max_count => limit, :skip => offset) + else + @repo.commits(@ref, limit, offset) + end respond_to do |format| format.html # index.html.erb @@ -29,8 +29,8 @@ class CommitsController < ApplicationController def show @commit = project.repo.commits(params[:id]).first - @notes = project.notes.where(:noteable_id => @commit.id, :noteable_type => "Commit").order("created_at DESC").limit(20) - @note = @project.notes.new(:noteable_id => @commit.id, :noteable_type => "Commit") + @notes = project.commit_notes(@commit).fresh.limit(20) + @note = @project.build_commit_note(@commit) respond_to do |format| format.html diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 5e00fe461e6..0494e1820de 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -6,8 +6,8 @@ class ProjectsController < ApplicationController before_filter :add_project_abilities before_filter :authorize_read_project!, :except => [:index, :new, :create] before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy] - before_filter :require_non_empty_project, :only => [:blob, :tree] + before_filter :load_refs, :only => :tree # load @branch, @tag & @ref def index source = current_user.projects @@ -101,15 +101,13 @@ class ProjectsController < ApplicationController # def tree - load_refs # load @branch, @tag & @ref - @repo = project.repo - if params[:commit_id] - @commit = @repo.commits(params[:commit_id]).first - else - @commit = @repo.commits(@ref).first - end + @commit = if params[:commit_id] + @repo.commits(params[:commit_id]).first + else + @repo.commits(@ref).first + end @tree = @commit.tree @tree = @tree / params[:path] if params[:path] diff --git a/app/models/project.rb b/app/models/project.rb index 9767352f1ee..e4448e786e9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -51,6 +51,10 @@ class Project < ActiveRecord::Base end delegate :repo, + :url_to_repo, + :path_to_repo, + :update_gitosis_project, + :destroy_gitosis_project, :tags, :repo_exists?, :commit, @@ -74,16 +78,12 @@ class Project < ActiveRecord::Base notes.where(:noteable_type => ["", nil]) end - def update_gitosis_project - Gitosis.new.configure do |c| - c.update_project(path, gitosis_writers) - end + def build_commit_note(commit) + notes.new(:noteable_id => commit.id, :noteable_type => "Commit") end - def destroy_gitosis_project - Gitosis.new.configure do |c| - c.destroy_project(self) - end + def commit_notes(commit) + notes.where(:noteable_id => commit.id, :noteable_type => "Commit") end def add_access(user, *access) @@ -121,14 +121,6 @@ class Project < ActiveRecord::Base private_flag end - def url_to_repo - "#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git" - end - - def path_to_repo - GITOSIS["base_path"] + path + ".git" - end - def last_activity updates(1).first rescue diff --git a/app/models/repository.rb b/app/models/repository.rb index da679d8bb9e..c01320e81d0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -9,24 +9,48 @@ class Repository @project = project end + def path + @path ||= project.path + end + + def project_id + project.id + end + def repo @repo ||= Grit::Repo.new(project.path_to_repo) end - def tags - repo.tags.map(&:name).sort.reverse + def url_to_repo + "#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git" + end + + def path_to_repo + GITOSIS["base_path"] + path + ".git" + end + + def update_gitosis_project + Gitosis.new.configure do |c| + c.update_project(path, project.gitosis_writers) + end + end + + def destroy_gitosis_project + Gitosis.new.configure do |c| + c.destroy_project(@project) + end end def repo_exists? repo rescue false end - def commit(commit_id = nil) - if commit_id - repo.commits(commit_id).first - else - repo.commits.first - end + def tags + repo.tags.map(&:name).sort.reverse + end + + def heads + @heads ||= repo.heads end def tree(fcommit, path = nil) @@ -35,6 +59,14 @@ class Repository path ? (tree / path) : tree end + def commit(commit_id = nil) + if commit_id + repo.commits(commit_id).first + else + repo.commits.first + end + end + def fresh_commits(n = 10) commits = heads.map do |h| repo.commits(h.name, n) @@ -47,10 +79,6 @@ class Repository commits[0...n] end - def heads - @heads ||= repo.heads - end - def commits_since(date) commits = heads.map do |h| repo.log(h.name, nil, :since => date) -- cgit v1.2.1 From e2d0f42adfc71ca1689cc3a7e83c1930ebc05363 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 20:42:01 -0300 Subject: fixing buttons --- app/assets/stylesheets/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index 9e093fd4314..567826f7eff 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -101,7 +101,7 @@ input.text{border: 1px solid #ccc; border-radius: 4px; display: block; padding: /* eo Forms */ /* Tables */ -table {width:100%; border: 1px solid #DEE2E3} +table {width:100%; border: 1px solid #DEE2E3; margin-bottom: 20px} table thead{ -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; @@ -144,7 +144,7 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF} background-image: -o-linear-gradient(#f7f7f7 7.6%, #d5d5d5); } -a.button, input.button, .lbutton { +a.button, input.button { font-weight: bold; padding: 10px 20px; text-align: center; -- cgit v1.2.1 From e5852d41cf9e511c32f89353700eed2d811087b6 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Thu, 10 Nov 2011 21:00:22 -0300 Subject: code pre fix --- app/assets/stylesheets/projects.css.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 29dc267407e..e97687ed114 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -576,6 +576,8 @@ td.code { table.highlighttable pre{ line-height:16px !important; font-size:12px !important; + text-align: right; + padding-right: 4px; } .project-refs-form { -- cgit v1.2.1 From 594451563ccbbc8663884e2d5b6f3ac435a3baf7 Mon Sep 17 00:00:00 2001 From: Staicu Ionut Date: Fri, 11 Nov 2011 08:13:43 +0200 Subject: - added notification for users with no ssh key - moved project urls into input fields to avoid text expanding out of the box (on very long url) - removed extra markup for header menu arrow (:before ftw!) - small style changes on url inputs --- app/assets/stylesheets/style.scss | 50 +++++++++++++++++++++++++--------- app/views/layouts/_head_panel.html.erb | 10 +++++-- app/views/layouts/project.html.haml | 3 +- app/views/projects/_tile.html.haml | 6 ++-- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index 9e093fd4314..cb258134a0f 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -444,7 +444,24 @@ float: right; } .account-box.hover{height: 138px;} -.account-box.hover .account-links, .account-box.hover .arrow-up{display: block;} + +.account-box:hover > .account-links{display: block;} +header .account-links{background: white; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); position:relative;} +header .account-links:before { + content: "."; + width:0; + height:0; + position:absolute; + border:5px solid transparent; + border-color:rgba(255,255,255,0); + border-bottom-color:#fafafa; + text-indent:-9999px; + top:-10px; + line-height:0; + right:10px; + z-index:10; +} + /* Inspired by http://maxvoltar.com/temp/nowplaying/ */ header .account-links{background: white; display: none; z-index: 100000; border-radius: 5px; width: 100px; position: absolute; right: 20px; top: 46px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); } @@ -477,18 +494,15 @@ header .account-links a:last-child{ border-bottom: 0; } -header a.arrow-up{ - display: none; - width: 0; - height: 0; - float: right; - margin-right: 26px; - margin-bottom: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - - border-bottom: 5px solid white; +#no_ssh_key_defined { + border:1px solid #ee8801; + margin:20px; + padding:20px; + background:#ffe3f0; + h2{margin:0;} + p {margin:10px 0 0;} } + /* eo Account Box */ input.search-input{float: left; text-shadow: none; width: 116px; background-image: url('icon-search.png') ; background-repeat: no-repeat; background-position: 10px; border-radius: 100px; border: 1px solid rgba(0,0,0,.7); box-shadow: 0 1px 0 rgba(255,255,255,.2), 0 2px 2px rgba(0,0,0,.4) inset ; background-color: #D2D5DA; background-color: rgba(255,255,255,.5); padding: 5px; padding-left: 26px; margin-top: 4px; margin-right: 10px } input.search-input:focus{ background-color: white; width: 216px;} @@ -553,7 +567,17 @@ body.project-page h2.icon span{ background-position: -78px -68px; } body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; } body.project-page .page-title{margin-bottom: 0} body.project-page .project-sidebar {width: 220px; left: 0; top: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; float: left; display: inline-block; background: #f7f7f7; padding: 20px 0 20px 2%; margin: 0; } -body.project-page .project-sidebar input.text.git-url{ font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 14px 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px;} + +body.project-page input.text.git-url, +body.projects-page input.text.git-url{ font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 5px 14px 5px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -43px;width:100%;border:0;} + +body.projects-page input.text.git-url {margin:10px 0 0 } + +.git_url_wrapper { margin-right:50px } + +.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .5); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .5); box-shadow:0px 0px 10px rgba(0, 0, 0, .5); } + + body.project-page .project-sidebar aside{width: 219px} body.project-page .project-sidebar aside a{display: block; position: relative; background: white; padding: 15px 10px; border-bottom: 1px solid #eee} body.project-page .project-sidebar aside a:first-child{ diff --git a/app/views/layouts/_head_panel.html.erb b/app/views/layouts/_head_panel.html.erb index 6db1d3897c5..20590e25ebb 100644 --- a/app/views/layouts/_head_panel.html.erb +++ b/app/views/layouts/_head_panel.html.erb @@ -8,8 +8,6 @@ <%= image_tag gravatar_icon(current_user.email) %> <% end %> - -
    @@ -55,4 +55,4 @@

    No SSH Key is defined. You won't be able to use any Git command!

    Click <%=link_to( 'here', keys_path ) %> to add one!

    -<% end %> \ No newline at end of file +<% end %> -- cgit v1.2.1 From 49b2fb24bc0a06b41c6ff328f8a8c63b26921c31 Mon Sep 17 00:00:00 2001 From: gitlabhq Date: Fri, 11 Nov 2011 11:35:51 -0500 Subject: remove fluiod layout --- app/controllers/application_controller.rb | 17 ----------------- app/helpers/application_helper.rb | 4 ++-- app/views/layouts/_head_panel.html.erb | 4 +--- app/views/projects/_recent_commits.html.haml | 2 +- app/views/projects/_recent_messages.html.haml | 4 ++-- app/views/projects/_tree_item.html.haml | 2 +- 6 files changed, 7 insertions(+), 26 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7fee598e325..2580f84ea6d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,6 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! - before_filter :view_style - protect_from_forgery - helper_method :abilities, :can? rescue_from Gitosis::AccessDenied do |exception| @@ -76,20 +73,6 @@ class ApplicationController < ActionController::Base redirect_to @project unless @project.repo_exists? end - def view_style - if params[:view_style] == "collapsed" - cookies[:view_style] = "collapsed" - elsif params[:view_style] == "fluid" - cookies[:view_style] = "fluid" - end - - @view_mode = if cookies[:view_style] == "fluid" - :fluid - else - :collapsed - end - end - def respond_with_notes if params[:last_id] && params[:first_id] @notes = @notes.where("id >= ?", params[:first_id]) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9c072f629b2..17c831daf80 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -7,7 +7,7 @@ module ApplicationHelper end def fixed_mode? - @view_mode == :fluid + true end def body_class(default_class = nil) @@ -15,7 +15,7 @@ module ApplicationHelper default_class : content_for(:body_class) - [main, @view_mode].join(" ") + [main, "collapsed"].join(" ") end def commit_name(project, commit) diff --git a/app/views/layouts/_head_panel.html.erb b/app/views/layouts/_head_panel.html.erb index 8815b17f334..7af6effd149 100644 --- a/app/views/layouts/_head_panel.html.erb +++ b/app/views/layouts/_head_panel.html.erb @@ -11,10 +11,8 @@ diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml index 5e9eca8d00a..7dfc1971213 100644 --- a/app/views/projects/_recent_commits.html.haml +++ b/app/views/projects/_recent_commits.html.haml @@ -22,7 +22,7 @@ - else = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" .title - %p= link_to truncate(commit.safe_message, :length => fixed_mode? ? 40 : 100), project_commit_path(@project, :id => commit.id) + %p= link_to truncate(commit.safe_message, :length => 40), project_commit_path(@project, :id => commit.id) %span %span.author diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml index 1a8c8c972e6..57583f5cd98 100644 --- a/app/views/projects/_recent_messages.html.haml +++ b/app/views/projects/_recent_messages.html.haml @@ -7,14 +7,14 @@ %thead %th %div{ :class => "recent_message_parent"} - = link_to(truncate(dashboard_feed_title(parent), :length => fixed_mode? ? 40 : 100 ), dashboard_feed_path(@project, parent)) + = link_to(truncate(dashboard_feed_title(parent), :length => 40 ), dashboard_feed_path(@project, parent)) - notes.sort {|x,y| y.updated_at <=> x.updated_at }.each do |note| %tr %td %div.message = image_tag gravatar_icon(note.author_email), :class => "left", :width => 40, :style => "padding-right:5px;" %div.title - = link_to markdown(truncate(note.note, :length => fixed_mode? ? 40 : 100)), dashboard_feed_path(@project, parent) + "#note_#{note.id}" + = link_to markdown(truncate(note.note, :length => 40)), dashboard_feed_path(@project, parent) + "#note_#{note.id}" - if note.attachment.url %br Attachment: diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 273d0272322..1637202cbd3 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -12,7 +12,7 @@ = time_ago_in_words(content_commit.committed_date) ago %td.commit - = link_to truncate(content_commit.safe_message, :length => fixed_mode? ? 40 : 80), project_commit_path(@project, content_commit), :class => "tree-commit-link" + = link_to truncate(content_commit.safe_message, :length => 40), project_commit_path(@project, content_commit), :class => "tree-commit-link" - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) - if tm = link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) -- cgit v1.2.1 From d5da9d32b5167cc99073d81548ffb6e95d2cfbe3 Mon Sep 17 00:00:00 2001 From: Ricardo Rauch Date: Fri, 11 Nov 2011 17:04:18 -0300 Subject: fixes css --- app/assets/stylesheets/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index 8567b0ed15b..b7bd6191ad5 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -573,7 +573,7 @@ body.projects-page input.text.git-url { font-size: 12px; border-radius: 5px; col body.projects-page input.text.git-url {margin:10px 0 0 } .git_url_wrapper { margin-right:50px } -.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .5); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .5); box-shadow:0px 0px 10px rgba(0, 0, 0, .5); } +.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); box-shadow:0px 0px 10px rgba(0, 0, 0, .1); } body.project-page .project-sidebar aside{width: 219px} -- cgit v1.2.1 From a1fff682de99618298883742c8cdc169c03c623f Mon Sep 17 00:00:00 2001 From: lukasberns Date: Sat, 12 Nov 2011 08:30:42 +0900 Subject: Add support for custom SSH ports --- app/models/repository.rb | 6 +++++- config/gitosis.yml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c01320e81d0..3b09471596c 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -22,7 +22,11 @@ class Repository end def url_to_repo - "#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git" + if !GITOSIS["port"] or GITOSIS["port"] == 22 + "#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git" + else + "ssh://#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{GITOSIS["port"]}/#{path}.git" + end end def path_to_repo diff --git a/config/gitosis.yml b/config/gitosis.yml index cf76f5a2f7e..1e3137e89e6 100644 --- a/config/gitosis.yml +++ b/config/gitosis.yml @@ -2,3 +2,4 @@ admin_uri: git@localhost:gitosis-admin.git base_path: /home/git/repositories/ host: localhost git_user: git +# port: 22 -- cgit v1.2.1 From 48a1e31010f331db1ec0d7947bfb10b1b3c700ec Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 12:41:34 +0200 Subject: snippet table restyle --- app/assets/stylesheets/projects.css.scss | 6 +++++- app/views/snippets/_snippet.html.haml | 22 ++++++++++++++-------- app/views/snippets/index.html.haml | 7 ++----- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 45bf317ef19..e92784f96df 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -622,13 +622,16 @@ body.project-page table .commit { #notes-list .note .delete-note { display:none; } #notes-list .note:hover .delete-note { display:block; } +.snippet .action-links, #issues-table-holder .issue .action-links { display:none; a { margin-left:10px; } } - + +.snippet:hover .action-links { display:block; } + .issue-number { float: left; border-radius: 5px; @@ -665,6 +668,7 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f //.message .note-title p { margin-bottom:0px; } .commit, +.snippet, .message { .title { color:#666; diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml index ddfba6bf80f..dc45e132c9f 100644 --- a/app/views/snippets/_snippet.html.haml +++ b/app/views/snippets/_snippet.html.haml @@ -2,11 +2,17 @@ %tr{ :id => dom_id(snippet), :class => "snippet", :url => project_snippet_path(@project, snippet) } %td = image_tag gravatar_icon(snippet.author.email), :class => "left", :width => 40, :style => "padding:0 5px;" - = truncate snippet.author.name, :lenght => 20 - %td= html_escape snippet.title - %td= html_escape snippet.file_name - %td - - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user - = link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "lbutton positive" - - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user - = link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{snippet.id}" + %span + %strong= html_escape snippet.title + %br + %br + %div.author + %strong= truncate snippet.author.name, :lenght => 20 + %cite.cgray + = time_ago_in_words(snippet.updated_at) + ago + .right.action-links + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "cgray" + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "cred delete-snippet negative", :id => "destroy_snippet_#{snippet.id}" diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index 3f2610004bf..58e6085f388 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -3,11 +3,8 @@ .left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm" %table.round-borders#snippets-table - %tr - %th Author - %th Title - %th File name - %th + %thead + %th = render @snippets.fresh :javascript $('.delete-snippet').live('ajax:success', function() { -- cgit v1.2.1 From de09e356934cb90546de7ebf1c4e721a31518235 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 12:42:05 +0200 Subject: refactored top panel spec --- spec/requests/top_panel_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/requests/top_panel_spec.rb b/spec/requests/top_panel_spec.rb index d1cea6d86d2..a5b5a2c9b35 100644 --- a/spec/requests/top_panel_spec.rb +++ b/spec/requests/top_panel_spec.rb @@ -7,8 +7,9 @@ describe "Top Panel", :js => true do before do visit projects_path fill_in "search", :with => "Ke" - sleep(2) - find(:xpath, "//ul[contains(@class,'ui-autocomplete')]/li/a[.=\"Keys\"]").click + within ".ui-autocomplete" do + find(:xpath, "//a[.=\"Keys\"]").click + end end it "should be on projects page" do @@ -23,8 +24,9 @@ describe "Top Panel", :js => true do visit project_path(@project) fill_in "search", :with => "Commi" - sleep(2) - find(:xpath, "//ul[contains(@class,'ui-autocomplete')]/li/a[.=\"#{@project.code} / Commits\"]").click + within ".ui-autocomplete" do + find(:xpath, "//a[.=\"#{@project.code} / Commits\"]").click + end end it "should be on projects page" do -- cgit v1.2.1 From 60f4576d5d5dd42e99f4bfbaf51603d167d1d344 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 12:50:25 +0200 Subject: top panel -> search -> css --- app/assets/stylesheets/projects.css.scss | 44 ----------------------- vendor/assets/stylesheets/jquery-ui/jquery-ui.css | 6 ++-- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index e92784f96df..3bc7492bf36 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -110,50 +110,6 @@ table.round-borders { background: transparent 9 !important; } -#header-panel { - @include panel-color; - height:40px; - position:fixed; - z-index:999; - top:0px; - width:100%; - margin-bottom:10px; - overflow:hidden; - .button{ - color:#bbb; - border:none; - margin:0px; - height:25px; - background:transparent; - padding:10px 20px 5px 20px; - &:hover{ - color:white; - } - - &.current { - border-bottom: 3px solid #EAEAEA !important; - padding: 10px 20px 0; - color: #eaeaea; - } - } - .search-holder { - float:left; - width:290px; - input { - @include round-borders-all(4px); - width:290px; - border-color:#888; - padding:5px; - background:#666; - color:#222; - &:focus { - background:#fff; - color:#000; - } - } - } -} - #content-container{ min-height:250px; background: #fff; diff --git a/vendor/assets/stylesheets/jquery-ui/jquery-ui.css b/vendor/assets/stylesheets/jquery-ui/jquery-ui.css index a94efee496d..357df7ab59f 100644 --- a/vendor/assets/stylesheets/jquery-ui/jquery-ui.css +++ b/vendor/assets/stylesheets/jquery-ui/jquery-ui.css @@ -382,15 +382,13 @@ padding:.2em .4em; line-height:1.5; zoom:1; + font-weight: normal; } .ui-menu .ui-menu-item a.ui-state-hover, .ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; - background: #fff !important; - background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#FFF6BF)) !important; - background: -moz-linear-gradient(top,#fff,#FFF6BF) !important; - background: transparent 9 !important; + background: #eee; border-radius:0px; border-color:white; border-bottom: 1px solid #E2EAEE; -- cgit v1.2.1 From ef088725341b2f202b7d33561d12efdf91b7f354 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 13:04:43 +0200 Subject: css refactor --- app/assets/stylesheets/commits.css.scss | 40 ++++++ app/assets/stylesheets/dashboard.css.scss | 3 - app/assets/stylesheets/issues.css.scss | 41 ++++++- app/assets/stylesheets/notes.css.scss | 44 +++++++ app/assets/stylesheets/profile.css.scss | 3 - app/assets/stylesheets/projects.css.scss | 197 ++---------------------------- app/assets/stylesheets/snippets.css.scss | 3 - 7 files changed, 135 insertions(+), 196 deletions(-) create mode 100644 app/assets/stylesheets/commits.css.scss delete mode 100644 app/assets/stylesheets/dashboard.css.scss create mode 100644 app/assets/stylesheets/notes.css.scss delete mode 100644 app/assets/stylesheets/profile.css.scss delete mode 100644 app/assets/stylesheets/snippets.css.scss diff --git a/app/assets/stylesheets/commits.css.scss b/app/assets/stylesheets/commits.css.scss new file mode 100644 index 00000000000..f1f09219bf2 --- /dev/null +++ b/app/assets/stylesheets/commits.css.scss @@ -0,0 +1,40 @@ +/** Commit diff view **/ +.diff_file { + border:1px solid #CCC; + margin-bottom:1em; + + .diff_file_header { + padding:5px 5px; + border-bottom:1px solid #CCC; + background: #eee; + } + .diff_file_content { + overflow:auto; + overflow-y:hidden; + background:#fff; + color:#333; + font-size: 12px; + font-family: 'Courier New', 'andale mono','lucida console',monospace; + } + .diff_file_content_image { + background:#eee; + text-align:center; + img { + padding:100px; + max-width:300px; + } + } +} + +.diff_file_content{ + .old_line, .new_line { + background:#ECECEC; + color:#777; + width:30px; + float:left; + padding: 0px 5px; + border-right: 1px solid #ccc; + } +} + + diff --git a/app/assets/stylesheets/dashboard.css.scss b/app/assets/stylesheets/dashboard.css.scss deleted file mode 100644 index e8f34fdd057..00000000000 --- a/app/assets/stylesheets/dashboard.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Dashboard controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/issues.css.scss b/app/assets/stylesheets/issues.css.scss index 1fbc8cb88df..2aef285bb5d 100644 --- a/app/assets/stylesheets/issues.css.scss +++ b/app/assets/stylesheets/issues.css.scss @@ -1,3 +1,38 @@ -// Place all the styles related to the Issues controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ +.issue-number { + float: left; + border-radius: 5px; + text-shadow: none; + background: rgba(0, 0, 0, 0.12); + text-align: center; + padding: 14px 8px; + width: 40px; + margin-right: 10px; + color: #444; +} + +#issues-table-holder .issue:hover .action-links { display:block; } + +.issues_filter { + margin-top:10px; + .left { + margin-right:15px; + } +} +.top_panel_issues{ + #issue_search_form { + margin:5px 0; + input { + border:1px solid #D3D3D3; + padding: 3px; + height: 28px; + width: 300px; + -webkit-appearance:none; + box-sizing: border-box; + -moz-box-sizing: border-box; + + &:focus { + border-color:#c2e1ef; + } + } + } +} diff --git a/app/assets/stylesheets/notes.css.scss b/app/assets/stylesheets/notes.css.scss new file mode 100644 index 00000000000..b3f32e10115 --- /dev/null +++ b/app/assets/stylesheets/notes.css.scss @@ -0,0 +1,44 @@ +/** Notes **/ + +#notes-list { + display:block; + list-style:none; + margin:0px; + padding:0px; +} + +.issue_notes { + .note_content { + float:left; + width:400px; + } +} + + +/* Note textare */ +#note_note { + height:100px; + width:97%; + font-size:14px; +} + +#new_note { + #note_note { + height:25px; + } + .attach_holder { + display:none; + } +} + +#notes-list .note .delete-note { display:none; } +#notes-list .note:hover .delete-note { display:block; } + +body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} +body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} +body.project-page #notes-list .note img{float: left; margin-right: 10px;} +body.project-page #notes-list .note span.note-title{display: block;} +body.project-page #notes-list .note span.note-title{margin-bottom: 10px} +body.project-page #notes-list .note span.note-author{color: #999; font-weight: normal; font-style: italic;} +body.project-page #notes-list .note span.note-author strong{font-weight: bold; font-style: normal;} + diff --git a/app/assets/stylesheets/profile.css.scss b/app/assets/stylesheets/profile.css.scss deleted file mode 100644 index 22ee5087687..00000000000 --- a/app/assets/stylesheets/profile.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Profile controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 3bc7492bf36..b03ad1059e0 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -4,26 +4,20 @@ @mixin round-borders-bottom($radius) { border-top: 1px solid #eaeaea; - -moz-border-radius-bottomright: $radius; -moz-border-radius-bottomleft: $radius; - border-bottom-right-radius: $radius; border-bottom-left-radius: $radius; - -webkit-border-bottom-left-radius: $radius; -webkit-border-bottom-right-radius: $radius; } @mixin round-borders-top($radius) { border-top: 1px solid #eaeaea; - -moz-border-radius-topright: $radius; -moz-border-radius-topleft: $radius; - border-top-right-radius: $radius; border-top-left-radius: $radius; - -webkit-border-top-left-radius: $radius; -webkit-border-top-right-radius: $radius; } @@ -36,45 +30,18 @@ } @mixin hover-color { - background: #fff !important; - background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#FFF6BF)) !important; - background: -moz-linear-gradient(top,#fff,#FFF6BF) !important; - background: transparent 9 !important; + background-color:#FFFFCF; } -.diff_file { - border:1px solid #CCC; - margin-bottom:1em; - - .diff_file_header { - padding:5px 5px; - border-bottom:1px solid #CCC; - background: #eee; - } - .diff_file_content { - overflow:auto; - overflow-y:hidden; - background:#fff; - color:#333; - font-size: 12px; - font-family: 'Courier New', 'andale mono','lucida console',monospace; - } - .diff_file_content_image { - background:#eee; - text-align:center; - img { - padding:100px; - max-width:300px; - } - } +@mixin panel-color { + background: #111 !important; + background: -webkit-gradient(linear,left top,left bottom,from(#333),to(#111)) !important; + background: -moz-linear-gradient(top,#333,#111) !important; + background: transparent 9 !important; } -#logo { - &:hover { - background:none; - } -} +/** File stat **/ .file_stats { margin-bottom:10px; @include round-borders-all(4px); @@ -103,12 +70,6 @@ table.round-borders { float:left; } -@mixin panel-color { - background: #111 !important; - background: -webkit-gradient(linear,left top,left bottom,from(#333),to(#111)) !important; - background: -moz-linear-gradient(top,#333,#111) !important; - background: transparent 9 !important; -} #content-container{ min-height:250px; @@ -123,17 +84,6 @@ a { color: #111; } -.diff_file_content{ - .old_line, .new_line { - background:#ECECEC; - color:#777; - width:30px; - float:left; - padding: 0px 5px; - border-right: 1px solid #ccc; - } -} - .view_file_content{ .old_line, .new_line { background:#ECECEC; @@ -263,36 +213,12 @@ input.ssh_project_url { border-width: 1px; } - -.top_menu_count { - background: none repeat scroll 0 0 white; - color: #333; - border-color: #4BB8D2; - padding: 2px; - font-size:10px; - border-top:none; - text-align:center; - float:right; - width:25px; - - -} - -#logo { - color: #EAEAEA; - font-family: monospace; - font-size: 26px; - padding: 4px; - text-decoration: none; - text-shadow: #555 1px 1px; -} - /** FLASH **/ #flash_container { height:40px; position:fixed; - z-index:1009; + z-index:1209; top:0px; width:100%; margin-bottom:10px; @@ -342,34 +268,6 @@ input.ssh_project_url { } } -/** Notes **/ - -#notes-list { - display:block; - list-style:none; - margin:0px; - padding:0px; -} - -.notes_count { - background: none repeat scroll 0 0 #FFF6BF; - border-color: #FFD324; - color: #514721; - border: 2px solid #DDDDDD; - margin-bottom: 1em; - margin-top: 3px; - padding: 2px 5px; - position: relative; - right: 6px; - top: 6px; -} - -.issue_notes { - .note_content { - float:left; - width:400px; - } -} #user_projects_limit{ width: 60px; @@ -400,24 +298,6 @@ input.ssh_project_url { } -/* Note textare */ -#note_note { - height:100px; - width:97%; - font-size:14px; -} - -#new_note { - #note_note { - height:25px; - } - .attach_holder { - display:none; - } -} - - - .tag { @include round-borders-all(4px); padding:2px 4px; @@ -448,33 +328,6 @@ input.ssh_project_url { } } -#issues-table .issue { - &.critical { - td { - //background: #D12F19; - //color:#fff; - } - } -} - -.top_panel_issues{ - #issue_search_form { - margin:5px 0; - input { - border:1px solid #D3D3D3; - padding: 3px; - height: 28px; - width: 300px; - -webkit-appearance:none; - box-sizing: border-box; - -moz-box-sizing: border-box; - - &:focus { - border-color:#c2e1ef; - } - } - } -} .left { float:left; @@ -553,12 +406,6 @@ table.highlighttable .linenodiv pre { width:200px; } -.issues_filter { - margin-top:10px; - .left { - margin-right:15px; - } -} .filter .left { margin-right:15px; } @@ -575,8 +422,6 @@ body.project-page table .commit { } } -#notes-list .note .delete-note { display:none; } -#notes-list .note:hover .delete-note { display:block; } .snippet .action-links, #issues-table-holder .issue .action-links { @@ -588,27 +433,6 @@ body.project-page table .commit { .snippet:hover .action-links { display:block; } -.issue-number { - float: left; - border-radius: 5px; - text-shadow: none; - background: rgba(0, 0, 0, 0.12); - text-align: center; - padding: 14px 8px; - width: 40px; - margin-right: 10px; - color: #444; -} - -#issues-table-holder .issue:hover .action-links { display:block; } - -body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} -body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} -body.project-page #notes-list .note img{float: left; margin-right: 10px;} -body.project-page #notes-list .note span.note-title{display: block;} -body.project-page #notes-list .note span.note-title{margin-bottom: 10px} -body.project-page #notes-list .note span.note-author{color: #999; font-weight: normal; font-style: italic;} -body.project-page #notes-list .note span.note-author strong{font-weight: bold; font-style: normal;} /** NEW PROJECT **/ @@ -640,3 +464,8 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f color: #999 } } + +/** UI autocomplete **/ +.ui-autocomplete { @include round-borders-all(5px); } +.ui-menu-item { cursor: pointer } + diff --git a/app/assets/stylesheets/snippets.css.scss b/app/assets/stylesheets/snippets.css.scss deleted file mode 100644 index 1b680d87bd3..00000000000 --- a/app/assets/stylesheets/snippets.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Snippets controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ -- cgit v1.2.1 From c1c64d985ec66433b9e990c5595997850727d8aa Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 14:45:32 +0200 Subject: css refactor --- app/assets/stylesheets/application.css | 31 ++++ app/assets/stylesheets/commits.css.scss | 4 +- app/assets/stylesheets/issues.css.scss | 14 +- app/assets/stylesheets/projects.css.scss | 304 +++++++++---------------------- app/views/notes/_form.html.haml | 2 +- app/views/snippets/_form.html.haml | 5 +- app/views/snippets/index.html.haml | 2 +- 7 files changed, 141 insertions(+), 221 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index e170dc07864..8deb3642a12 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -8,3 +8,34 @@ *= require_self *= require_tree . */ + +/** COLORS **/ +.cgray { color:gray; } +.cred { color:#D12F19; } +.cgreen { color:#44aa22; } + +/** COMMON STYLES **/ +.left { + float:left; +} +.right { + float:right; +} +.width-50p{ + width:50%; +} +.width-49p{ + width:49%; +} +.width-30p{ + width:30%; +} +.width-65p{ + width:65%; +} +.append-bottom-10 { + margin-bottom:10px; +} +.prepend-top-10 { + margin-top:10px; +} diff --git a/app/assets/stylesheets/commits.css.scss b/app/assets/stylesheets/commits.css.scss index f1f09219bf2..c56492cdfd0 100644 --- a/app/assets/stylesheets/commits.css.scss +++ b/app/assets/stylesheets/commits.css.scss @@ -37,4 +37,6 @@ } } - +pre.commit_message { + white-space: pre-wrap; +} diff --git a/app/assets/stylesheets/issues.css.scss b/app/assets/stylesheets/issues.css.scss index 2aef285bb5d..f520e37a68c 100644 --- a/app/assets/stylesheets/issues.css.scss +++ b/app/assets/stylesheets/issues.css.scss @@ -10,14 +10,13 @@ color: #444; } -#issues-table-holder .issue:hover .action-links { display:block; } - .issues_filter { margin-top:10px; .left { margin-right:15px; } } + .top_panel_issues{ #issue_search_form { margin:5px 0; @@ -36,3 +35,14 @@ } } } + +/** ISSUES LIST **/ +.issue .action-links { + display:none; + a { + margin-left:10px; + } +} +.issue:hover .action-links { display:block; } + + diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index b03ad1059e0..1a9ab673a95 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -1,7 +1,4 @@ -// Place all the styles related to the Projects controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ - +/** MIXINS **/ @mixin round-borders-bottom($radius) { border-top: 1px solid #eaeaea; -moz-border-radius-bottomright: $radius; @@ -29,18 +26,6 @@ border-radius: $radius; } -@mixin hover-color { - background-color:#FFFFCF; -} - -@mixin panel-color { - background: #111 !important; - background: -webkit-gradient(linear,left top,left bottom,from(#333),to(#111)) !important; - background: -moz-linear-gradient(top,#333,#111) !important; - background: transparent 9 !important; -} - - /** File stat **/ .file_stats { margin-bottom:10px; @@ -66,24 +51,16 @@ @include round-borders-all(4px); padding: 4px 0px; } + table.round-borders { float:left; } - -#content-container{ - min-height:250px; - background: #fff; - @include round-borders-bottom(8px); - borders:2px solid #eaeaea; - border-top: none; - padding:20px; -} - a { color: #111; } +/** FILE CONTENT VIEW **/ .view_file_content{ .old_line, .new_line { background:#ECECEC; @@ -122,10 +99,34 @@ a { } } -.back_small.button{ +td.code { + width: 100%; + .highlight { + margin-left: 55px; + overflow:auto; + overflow-y:hidden; + } +} +.highlight pre { + white-space: pre; + word-wrap:normal; +} +.highlighttable tr:hover { + background:white; +} +table.highlighttable pre{ + line-height:16px !important; + font-size:12px !important; } + +table.highlighttable .linenodiv pre { + text-align: right; + padding-right: 4px; +} + +/** PROJECTS **/ input.ssh_project_url { padding:5px; margin:0px; @@ -149,38 +150,7 @@ input.ssh_project_url { clear: both; } -.top_project_menu { - a { - border-right: 1px solid #FFFFFF; - box-shadow: -1px 0 #DDDDDD inset; - color: #666; - display: block; - font-size: 16px; - text-decoration: none; - line-height: 20px; - padding: 11px 26px 12px 24px; - text-shadow: 0 1px 0 #FFFFFF; - float:left; - - &.current { - background-color: #FFFFFF; - color: #222222; - } - } -} - -.top_bar { - margin-top:50px; - background-color: #F4F4F4; - @include round-borders-top(8px); - box-shadow: 0 1px #FFFFFF inset, 0 -1px #DDDDDD inset; - height: 43px; - overflow: hidden; - width:990px; -} - /** FORM INPUTS **/ - .user_new, .new_key, .new_issue, @@ -202,7 +172,6 @@ input.ssh_project_url { } .input_button { - //@include round-borders-all(4px); padding:8px; font-size:14px; cursor:pointer; @@ -214,7 +183,6 @@ input.ssh_project_url { } /** FLASH **/ - #flash_container { height:40px; position:fixed; @@ -236,7 +204,6 @@ input.ssh_project_url { } /** Buttons **/ - .lbutton, .lite_button { display:block; @@ -273,126 +240,10 @@ input.ssh_project_url { width: 60px; } -.project_thumb { - margin:20px 0; - width: 250px; - float:left; - padding:20px; - text-align:center; - p, h4 { - text-align:left; - } - .lbutton { - float:left; - } -} - .handle:hover{ cursor: move; } -.handle{ - width: 12px; - height: 12px; - padding: 10px; -} - - -.tag { - @include round-borders-all(4px); - padding:2px 4px; - border:none; - text-shadow:none; - - &.high { - background: #D12F19; - color:white; - } - - &.today { - background: #44aa22; - color:white; - } - - &.yours { - background: #4466cc; - color:white; - } - &.normal { - background: #2c5ca6; - color:white; - } - &.notes { - background: #2c5c66; - color:white; - } -} - - -.left { - float:left; -} -.right { - float:right; -} - -.width-50p{ - width:50%; -} -.width-49p{ - width:49%; -} -.width-30p{ - width:30%; -} -.width-65p{ - width:65%; -} -pre.commit_message { - white-space: pre-wrap; -} - -#container { -/* min-height:100%;*/ -} -.ui-selectmenu{ - @include round-borders-all(4px); - margin-right:10px; - font-size:1.5em; - height:auto; - font-weight:bold; - .ui-selectmenu-status { - padding:3px 10px; - } -} - -td.code { - width: 100%; - .highlight { - margin-left: 55px; - overflow:auto; - overflow-y:hidden; - } -} -.highlight pre { - white-space: pre; - word-wrap:normal; -} - -.highlighttable tr:hover { - background:white; -} -table.highlighttable pre{ - line-height:16px !important; - font-size:12px !important; -} - - -table.highlighttable .linenodiv pre { - text-align: right; - padding-right: 4px; -} - .project-refs-form { span { background: none !important; @@ -408,11 +259,6 @@ table.highlighttable .linenodiv pre { .filter .left { margin-right:15px; } - -.cgray { color:gray; } -.cred { color:#D12F19; } -.cgreen { color:#44aa22; } - body.project-page table .commit { a.tree-commit-link { color:gray; @@ -422,50 +268,80 @@ body.project-page table .commit { } } - -.snippet .action-links, -#issues-table-holder .issue .action-links { - display:none; - a { - margin-left:10px; - } -} - -.snippet:hover .action-links { display:block; } - - - /** NEW PROJECT **/ .new-project-hodler { - .icon span { - background-position: -31px -70px; - } - td { - border-bottom: 1px solid #DEE2E3; - } + .icon span { background-position: -31px -70px; } + td { border-bottom: 1px solid #DEE2E3; } } -//.message .note-title p { margin-bottom:0px; } - +/** Feed entry **/ .commit, .snippet, .message { .title { color:#666; - a { - color:#666 !important; - } - p { - margin-top:0px; - } - } - - .author { - color: #999 + a { color:#666 !important; } + p { margin-top:0px; } } + .author { color: #999 } } -/** UI autocomplete **/ +/** JQuery UI **/ .ui-autocomplete { @include round-borders-all(5px); } .ui-menu-item { cursor: pointer } +.ui-selectmenu{ + @include round-borders-all(4px); + margin-right:10px; + font-size:1.5em; + height:auto; + font-weight:bold; + .ui-selectmenu-status { + padding:3px 10px; + } +} +/** Snippets **/ +.new_snippet textarea, +.edit_snippet textarea { + height:300px; + padding: 8px; + width: 95%; +} +.snippet .action-links { + display:none; + a { + margin-left:10px; + } +} +.snippet:hover .action-links { display:block; } + +/** ISSUES TAGS **/ +.tag { + @include round-borders-all(4px); + padding:2px 4px; + border:none; + text-shadow:none; + + &.high { + background: #D12F19; + color:white; + } + + &.today { + background: #44aa22; + color:white; + } + + &.yours { + background: #4466cc; + color:white; + } + &.normal { + background: #2c5ca6; + color:white; + } + &.notes { + background: #2c5c66; + color:white; + } +} diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml index a0def20b32d..98e66bd4cd1 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -28,4 +28,4 @@ .clear %br - = f.submit 'Add note', :class => "lbutton vm", :id => "submit_note" + = f.submit 'Add note', :class => "button", :id => "submit_note" diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml index 5cd0f74a0be..b3cae65c3e9 100644 --- a/app/views/snippets/_form.html.haml +++ b/app/views/snippets/_form.html.haml @@ -19,7 +19,8 @@ %td{:colspan => 2} = f.label :content, "Code" %br - = f.text_area :content, :style => "height:240px;width:932px;" + %br + = f.text_area :content .actions.prepend-top - = f.submit 'Save', :class => "lbutton vm" + = f.submit 'Save', :class => "button" diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index 58e6085f388..0ae37cfff63 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -1,6 +1,6 @@ %div - if can? current_user, :write_snippet, @project - .left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm" + = link_to 'New Snippet', new_project_snippet_path(@project), :class => "button append-bottom-10" %table.round-borders#snippets-table %thead -- cgit v1.2.1 From 4dd5d9c8cce5c596a159277c464026cf0f50f1c4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 15:18:56 +0200 Subject: =?UTF-8?q?Issue=20#185=20=E2=80=93=20Show=20branch=20name=20for?= =?UTF-8?q?=20commits=20on=20activities=20&=20dashboard=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/stylesheets/projects.css.scss | 8 ++++++++ app/models/repository.rb | 4 ++-- app/views/dashboard/index.html.haml | 2 ++ app/views/layouts/project.html.haml | 2 +- app/views/projects/_recent_commits.html.haml | 4 +++- lib/commit_ext.rb | 2 ++ spec/requests/dashboard_spec.rb | 1 + spec/requests/projects_spec.rb | 9 ++++++--- 8 files changed, 25 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 1a9ab673a95..c94e7934ba3 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -344,4 +344,12 @@ body.project-page table .commit { background: #2c5c66; color:white; } + &.issue { + background: #D12F19; + color:white; + } + &.commit { + background: #2c5c66; + color:white; + } } diff --git a/app/models/repository.rb b/app/models/repository.rb index 3b09471596c..5a2b71a4c5e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -73,7 +73,7 @@ class Repository def fresh_commits(n = 10) commits = heads.map do |h| - repo.commits(h.name, n) + repo.commits(h.name, n).each { |c| c.head = h } end.flatten.uniq { |c| c.id } commits.sort! do |x, y| @@ -85,7 +85,7 @@ class Repository def commits_since(date) commits = heads.map do |h| - repo.log(h.name, nil, :since => date) + repo.log(h.name, nil, :since => date).each { |c| c.head = h } end.flatten.uniq { |c| c.id } commits.sort! do |x, y| diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index e7559dd5762..a8b7f2538d3 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -27,6 +27,8 @@ %a.project-update{:href => dashboard_feed_path(project, update)} = image_tag gravatar_icon(update.author_email), :class => "left", :width => 40 %span.update-title + - if update.kind_of?(Grit::Commit) + %span.tag.commit= update.head.name = dashboard_feed_title(update) %span.update-author %strong= update.author_name diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 078f710c6c6..48b4360f687 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -19,7 +19,7 @@ .git_url_wrapper %input.git-url.text{:id => "", :name => "", :readonly => "", :type => "text", :value => @project.url_to_repo, :class => "one_click_select"} %aside - = link_to "History", project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil + = link_to "Activities", project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil = link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil = link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil = link_to team_project_path(@project), :class => (current_page?(:controller => "projects", :action => "team", :id => @project) || controller.controller_name == "team_members") ? "current" : nil do diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml index 7dfc1971213..9680eb52208 100644 --- a/app/views/projects/_recent_commits.html.haml +++ b/app/views/projects/_recent_commits.html.haml @@ -22,7 +22,9 @@ - else = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" .title - %p= link_to truncate(commit.safe_message, :length => 40), project_commit_path(@project, :id => commit.id) + %p + %span.tag.commit= commit.head.name + = link_to truncate(commit.safe_message, :length => 40), project_commit_path(@project, :id => commit.id) %span %span.author diff --git a/lib/commit_ext.rb b/lib/commit_ext.rb index db6503557ae..2850a239544 100644 --- a/lib/commit_ext.rb +++ b/lib/commit_ext.rb @@ -1,4 +1,6 @@ module CommitExt + attr_accessor :head + def safe_message message.encode("UTF-8", :invalid => :replace, diff --git a/spec/requests/dashboard_spec.rb b/spec/requests/dashboard_spec.rb index 6940366af41..d54c6154b55 100644 --- a/spec/requests/dashboard_spec.rb +++ b/spec/requests/dashboard_spec.rb @@ -22,6 +22,7 @@ describe "Dashboard" do it "should have news feed" do within "#news-feed" do + page.should have_content("master") page.should have_content(@project.commit.author.name) page.should have_content(@project.commit.safe_message) end diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb index 3d8971579b9..87f76fdb3a7 100644 --- a/spec/requests/projects_spec.rb +++ b/spec/requests/projects_spec.rb @@ -72,10 +72,13 @@ describe "Projects" do current_path.should == project_path(@project) end - it "should beahave like dashboard" do - page.should have_content("History") + it "should beahave like activities page" do + within ".commit" do + page.should have_content("master") + page.should have_content(@project.commit.author.name) + page.should have_content(@project.commit.safe_message) + end end - end describe "GET /projects/team" do -- cgit v1.2.1 From 8786aff25a8e68e1dd6c50f3802eadb35908f151 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 16:19:34 +0200 Subject: project avtivity page --- app/assets/stylesheets/projects.css.scss | 22 ++++++++++++++++- app/views/projects/_feed.html.haml | 15 ++++++++++++ app/views/projects/_recent_commits.html.haml | 34 --------------------------- app/views/projects/_recent_messages.html.haml | 27 --------------------- app/views/projects/show.html.haml | 12 ++++++---- spec/requests/projects_spec.rb | 2 +- 6 files changed, 45 insertions(+), 67 deletions(-) create mode 100644 app/views/projects/_feed.html.haml delete mode 100644 app/views/projects/_recent_commits.html.haml delete mode 100644 app/views/projects/_recent_messages.html.haml diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index c94e7934ba3..8ea868bcf91 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -344,12 +344,32 @@ body.project-page table .commit { background: #2c5c66; color:white; } + &.note { + background: #2c5c66; + color:white; + } &.issue { background: #D12F19; color:white; } &.commit { - background: #2c5c66; + background: #44aacc; color:white; } } + + +/* Project Dashboard Page */ +html, body { height: 100%; } + +body.dashboard.project-page .news-feed h2{float: left;} +body.dashboard.project-page .news-feed .project-updates {margin-bottom: 20px; display: block; width: 100%;} +body.dashboard.project-page .news-feed .project-updates .data{ padding: 0} +body.dashboard.project-page .news-feed .project-updates a.project-update {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;} +body.dashboard.project-page .news-feed .project-updates a.project-update:last-child{border-bottom: 0} +body.dashboard.project-page .news-feed .project-updates a.project-update img{float: left; margin-right: 10px;} +body.dashboard.project-page .news-feed .project-updates a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;} +body.dashboard.project-page .news-feed .project-updates a.project-update span.update-title{margin-bottom: 10px} +body.dashboard.project-page .news-feed .project-updates a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;} +body.dashboard.project-page .news-feed .project-updates a.project-update span.update-author strong{font-weight: bold; font-style: normal;} +/* eo Dashboard Page */ diff --git a/app/views/projects/_feed.html.haml b/app/views/projects/_feed.html.haml new file mode 100644 index 00000000000..e1ed2944b26 --- /dev/null +++ b/app/views/projects/_feed.html.haml @@ -0,0 +1,15 @@ +%a.project-update{:href => dashboard_feed_path(project, update)} + = image_tag gravatar_icon(update.author_email), :class => "left", :width => 40 + %span.update-title + = dashboard_feed_title(update) + %span.update-author + %strong= update.author_name + authored + = time_ago_in_words(update.created_at) + ago + .right + - klass = update.class.to_s.split("::").last.downcase + %span.tag{ :class => klass }= klass + - if update.kind_of?(Grit::Commit) + %span.tag.commit= update.head.name + diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml deleted file mode 100644 index 9680eb52208..00000000000 --- a/app/views/projects/_recent_commits.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -%table - %thead - %th - Commits - .filter.right - = form_tag project_path(@project), :method => :get, :class => "right" do - .left - = radio_button_tag :view, "recent", (params[:view] || "recent") == "recent", :onclick => "this.form.submit()", :id => "recent_view" - = label_tag "recent_view","Recent" - .left - = radio_button_tag :view, "day", params[:view] == "day", :onclick => "this.form.submit()", :id => "day_view" - = label_tag "day_view","Today" - .left - = radio_button_tag :view, "week", params[:view] == "week", :onclick => "this.form.submit()", :id => "week_view" - = label_tag "week_view","Week" - - @commits.each do |commit| - %tr - %td - %div.commit - - if commit.author.email - = image_tag gravatar_icon(commit.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" - - else - = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" - .title - %p - %span.tag.commit= commit.head.name - = link_to truncate(commit.safe_message, :length => 40), project_commit_path(@project, :id => commit.id) - - %span - %span.author - %strong= commit.author.name.force_encoding("UTF-8") - %cite.cgray - = time_ago_in_words(commit.committed_date) - ago diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml deleted file mode 100644 index 57583f5cd98..00000000000 --- a/app/views/projects/_recent_messages.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -- @messages.group_by{ |x| [x.noteable_id, x.noteable_type]}.each do |item, notes| - - id, type = item[0], item[1] - - parent = load_note_parent(id, type, @project) - - next unless parent - - %table - %thead - %th - %div{ :class => "recent_message_parent"} - = link_to(truncate(dashboard_feed_title(parent), :length => 40 ), dashboard_feed_path(@project, parent)) - - notes.sort {|x,y| y.updated_at <=> x.updated_at }.each do |note| - %tr - %td - %div.message - = image_tag gravatar_icon(note.author_email), :class => "left", :width => 40, :style => "padding-right:5px;" - %div.title - = link_to markdown(truncate(note.note, :length => 40)), dashboard_feed_path(@project, parent) + "#note_#{note.id}" - - if note.attachment.url - %br - Attachment: - = link_to note.attachment_identifier, note.attachment.url - %div.author - %strong= note.author_name - %cite.cgray - = time_ago_in_words(note.updated_at) - ago - %br diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index f693cb9d394..97ac92d149d 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,8 +1,12 @@ -.left.width-49p - =render "projects/recent_commits" +- content_for(:body_class, "project-page dashboard") -.right.width-49p - =render "projects/recent_messages" +#news-feed.news-feed + %h2.icon + %span> + Activities + .project-box.project-updates.ui-box.ui-box-small.ui-box-big + - @project.updates(20).each do |update| + = render "projects/feed", :update => update, :project => @project :javascript function updateDashboard(){ diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb index 87f76fdb3a7..85c2d27525b 100644 --- a/spec/requests/projects_spec.rb +++ b/spec/requests/projects_spec.rb @@ -73,7 +73,7 @@ describe "Projects" do end it "should beahave like activities page" do - within ".commit" do + within ".project-update" do page.should have_content("master") page.should have_content(@project.commit.author.name) page.should have_content(@project.commit.safe_message) -- cgit v1.2.1 From 2f790001a029144f7a614d49a123cd1037f6398c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 12 Nov 2011 16:28:14 +0200 Subject: project -> show refactor --- app/controllers/projects_controller.rb | 17 ++--------------- app/views/dashboard/index.html.haml | 2 +- app/views/projects/show.html.haml | 2 +- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0494e1820de..0639982c6e7 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -64,21 +64,8 @@ class ProjectsController < ApplicationController def show return render "projects/empty" unless @project.repo_exists? - @date = case params[:view] - when "week" then Date.today - 7.days - when "day" then Date.today - else nil - end - - if @date - @date = @date.at_beginning_of_day - - @commits = @project.commits_since(@date) - @messages = project.notes.since(@date).order("created_at DESC") - else - @commits = @project.fresh_commits - @messages = project.notes.fresh.limit(10) - end + limit = (params[:limit] || 40).to_i + @activities = @project.updates(limit) end # diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index a8b7f2538d3..e25ec249ef5 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -28,7 +28,7 @@ = image_tag gravatar_icon(update.author_email), :class => "left", :width => 40 %span.update-title - if update.kind_of?(Grit::Commit) - %span.tag.commit= update.head.name + %span.right.tag.commit= update.head.name = dashboard_feed_title(update) %span.update-author %strong= update.author_name diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 97ac92d149d..0530736fb4c 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -5,7 +5,7 @@ %span> Activities .project-box.project-updates.ui-box.ui-box-small.ui-box-big - - @project.updates(20).each do |update| + - @activities.each do |update| = render "projects/feed", :update => update, :project => @project :javascript -- cgit v1.2.1 From 6b66a766d1e534dbfb5735828d9f6d770732a19b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sun, 13 Nov 2011 00:30:51 +0200 Subject: Graph: build json --- .rbenv-version | 1 + app/assets/javascripts/application.js | 4 +- app/controllers/projects_controller.rb | 40 ++++++++ app/views/layouts/project.html.haml | 1 + config/routes.rb | 1 + lib/assets/javascripts/branch-graph.js | 170 +++++++++++++++++++++++++++++++++ lib/commit_ext.rb | 3 + lib/graph_commit.rb | 82 ++++++++++++++++ vendor/assets/javascripts/raphael.js | 7 ++ 9 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 .rbenv-version create mode 100644 lib/assets/javascripts/branch-graph.js create mode 100644 lib/graph_commit.rb create mode 100644 vendor/assets/javascripts/raphael.js diff --git a/.rbenv-version b/.rbenv-version new file mode 100644 index 00000000000..0a95b9f5c71 --- /dev/null +++ b/.rbenv-version @@ -0,0 +1 @@ +1.9.2-p290 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4196f8a44db..7af4e61bbc0 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,6 +10,8 @@ //= require jquery.ui.selectmenu //= require jquery.tagify //= require jquery.cookie +//= require raphael +//= require branch-graph //= require_tree . $(function(){ @@ -35,4 +37,4 @@ function showMenu() { function resetMenu() { $(this).removeClass("hover"); -} \ No newline at end of file +} diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0494e1820de..a0e727954d8 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,3 +1,5 @@ +require File.join(Rails.root, 'lib', 'graph_commit') + class ProjectsController < ApplicationController before_filter :project, :except => [:index, :new, :create] layout :determine_layout @@ -125,6 +127,36 @@ class ProjectsController < ApplicationController return render_404 end + def graph + @repo = project.repo + commits = Grit::Commit.find_all(@repo, nil, {:max_count => 650}) + ref_cache = {} + commits.collect! do |commit| + add_refs(commit, ref_cache) + GraphCommit.new(commit) + end + + days = GraphCommit.index_commits(commits) + @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json + @commits_json = commits.collect do |c| + h = {} + h[:parents] = c.parents.collect do |p| + [p.id,0,0] + end + h[:author] = c.author.name.force_encoding("UTF-8") + h[:time] = c.time + h[:space] = c.space + h[:refs] = c.refs.collect{|r|r.name}.join(" ") unless c.refs.nil? + h[:id] = c.sha + h[:date] = c.date + h[:message] = c.message.force_encoding("UTF-8") + h[:email] = c.author.email + h + end.to_json + + render :text => @commits_json + end + def blob @repo = project.repo @commit = project.commit(params[:commit_id]) @@ -149,6 +181,14 @@ class ProjectsController < ApplicationController protected + def add_refs(commit, ref_cache) + if ref_cache.empty? + @repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref} + end + commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id + commit.refs ||= [] + end + def project @project ||= Project.find_by_code(params[:id]) end diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 078f710c6c6..ed289707af3 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -22,6 +22,7 @@ = link_to "History", project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil = link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil = link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil + = link_to "Network graph", graph_project_path(@project), :class => current_page?(:controller => "projects", :action => "graph", :project_id => @project) ? "current" : nil = link_to team_project_path(@project), :class => (current_page?(:controller => "projects", :action => "team", :id => @project) || controller.controller_name == "team_members") ? "current" : nil do Team - if @project.users_projects.count > 0 diff --git a/config/routes.rb b/config/routes.rb index d4e27965716..aaeddbf0283 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -34,6 +34,7 @@ Gitlab::Application.routes.draw do get "blob" get "team" get "wall" + get "graph" # tree viewer get "tree/:commit_id" => "projects#tree" diff --git a/lib/assets/javascripts/branch-graph.js b/lib/assets/javascripts/branch-graph.js new file mode 100644 index 00000000000..b98c836ff5c --- /dev/null +++ b/lib/assets/javascripts/branch-graph.js @@ -0,0 +1,170 @@ +var commits = chunk1.commits, + comms = {}, + pixelsX = [], + pixelsY = [], + mmax = Math.max, + mtime = 0, + mspace = 0, + parents = {}; +for (var i = 0, ii = commits.length; i < ii; i++) { + for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { + parents[commits[i].parents[j][0]] = true; + } + mtime = Math.max(mtime, commits[i].time); + mspace = Math.max(mspace, commits[i].space); +} +mtime = mtime + 4; +mspace = mspace + 10; +for (i = 0; i < ii; i++) { + if (commits[i].id in parents) { + commits[i].isParent = true; + } + comms[commits[i].id] = commits[i]; +} +var colors = ["#000"]; +for (var k = 0; k < mspace; k++) { + colors.push(Raphael.getColor()); +} +function branchGraph(holder) { + var ch = mspace * 20 + 20, cw = mtime * 20 + 20, + r = Raphael("holder", cw, ch), + top = r.set(); + var cuday = 0, cumonth = ""; + r.rect(0,0,days.length*20+20,40).attr({fill: "#999"}); + + for (mm = 0; mm < days.length; mm++) { + if(days[mm] != null){ + if(cuday != days[mm][0]){ + r.text(10+mm*20,30,days[mm][0]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); + cuday = days[mm][0] + } + if(cumonth != days[mm][1]){ + r.text(10+mm*20,10,days[mm][1]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); + cumonth = days[mm][1] + } + + } + } + for (i = 0; i < ii; i++) { + var x = 10 + 20 * commits[i].time, + y = 70 + 20 * commits[i].space; + r.circle(x, y, 3).attr({fill: colors[commits[i].space], stroke: "none"}); + if (commits[i].refs != null && commits[i].refs != "") { + var longrefs = commits[i].refs + var shortrefs = commits[i].refs; + if (shortrefs.length > 15){ + shortrefs = shortrefs.substr(0,13) + "..."; + } + var t = r.text(x+5,y+5,shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666", + title: longrefs, cursor: "pointer", rotation: "90"}); + + var textbox = t.getBBox(); + t.translate(textbox.height/-4,textbox.width/2); + } + for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { + var c = comms[commits[i].parents[j][0]]; + if (c) { + var cx = 10 + 20 * c.time, + cy = 70 + 20 * c.space; + if (c.space == commits[i].space) { + r.path("M" + (x - 5) + "," + (y + .0001) + "L" + (15 + 20 * c.time) + "," + (y + .0001)) + .attr({stroke: colors[c.space], "stroke-width": 2}); + + } else if (c.space < commits[i].space) { + r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C",x-5,y,x -17, y+2, x -20, y-10,"L", cx,y-10,cx , cy]) + .attr({stroke: colors[commits[i].space], "stroke-width": 2}); + } else { + r.path(["M", x-5, y, "l-5-2,0,4,5,-2C",x-5,y,x -17, y-2, x -20, y+10,"L", cx,y+10,cx , cy]) + .attr({stroke: colors[commits[i].space], "stroke-width": 2}); + } + } + } + (function (c, x, y) { + top.push(r.circle(x, y, 10).attr({fill: "#000", opacity: 0, cursor: "pointer"}) + .hover(function () { + var s = r.text(100, 100,c.author + "\n \n" +c.id + "\n \n" + c.message).attr({fill: "#fff"}); + this.popup = r.popupit(x, y + 5, s, 0); + top.push(this.popup.insertBefore(this)); + }, function () { + this.popup && this.popup.remove() && delete this.popup; + })); + }(commits[i], x, y)); + } + top.toFront(); + var hw = holder.offsetWidth, + hh = holder.offsetHeight, + v = r.rect(hw - 8, 0, 4, Math.pow(hh, 2) / ch, 2).attr({fill: "#000", opacity: 0}), + h = r.rect(0, hh - 8, Math.pow(hw, 2) / cw, 4, 2).attr({fill: "#000", opacity: 0}), + bars = r.set(v, h), + drag, + dragger = function (e) { + if (drag) { + e = e || window.event; + holder.scrollLeft = drag.sl - (e.clientX - drag.x); + holder.scrollTop = drag.st - (e.clientY - drag.y); + } + }; + holder.onmousedown = function (e) { + e = e || window.event; + drag = {x: e.clientX, y: e.clientY, st: holder.scrollTop, sl: holder.scrollLeft}; + document.onmousemove = dragger; + bars.animate({opacity: .5}, 300); + }; + document.onmouseup = function () { + drag = false; + document.onmousemove = null; + bars.animate({opacity: 0}, 300); + }; + holder.scrollLeft = cw; +}; +Raphael.fn.popupit = function (x, y, set, dir, size) { + dir = dir == null ? 2 : dir; + size = size || 5; + x = Math.round(x); + y = Math.round(y); + var bb = set.getBBox(), + w = Math.round(bb.width / 2), + h = Math.round(bb.height / 2), + dx = [0, w + size * 2, 0, -w - size * 2], + dy = [-h * 2 - size * 3, -h - size, 0, -h - size], + p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, + "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size, + "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, + "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size, + "l", -mmax(w - size, 0), 0, "z"].join(","), + xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir]; + set.translate(xy.x - w - bb.x, xy.y - h - bb.y); + return this.set(this.path(p).attr({fill: "#234", stroke: "none"}).insertBefore(set.node ? set : set[0]), set); +}; +Raphael.fn.popup = function (x, y, text, dir, size) { + dir = dir == null ? 2 : dir > 3 ? 3 : dir; + size = size || 5; + text = text || "$9.99"; + var res = this.set(), + d = 3; + res.push(this.path().attr({fill: "#000", stroke: "#000"})); + res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff", "font-family": "Helvetica, Arial"})); + res.update = function (X, Y, withAnimation) { + X = X || x; + Y = Y || y; + var bb = this[1].getBBox(), + w = bb.width / 2, + h = bb.height / 2, + dx = [0, w + size * 2, 0, -w - size * 2], + dy = [-h * 2 - size * 3, -h - size, 0, -h - size], + p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, + "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size, + "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, + "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size, + "l", -mmax(w - size, 0), 0, "z"].join(","), + xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir]; + xy.path = p; + if (withAnimation) { + this.animate(xy, 500, ">"); + } else { + this.attr(xy); + } + return this; + }; + return res.update(x, y); +}; diff --git a/lib/commit_ext.rb b/lib/commit_ext.rb index db6503557ae..6606ee46b49 100644 --- a/lib/commit_ext.rb +++ b/lib/commit_ext.rb @@ -1,4 +1,7 @@ module CommitExt + + attr_accessor :refs + def safe_message message.encode("UTF-8", :invalid => :replace, diff --git a/lib/graph_commit.rb b/lib/graph_commit.rb new file mode 100644 index 00000000000..ee765cb4ef2 --- /dev/null +++ b/lib/graph_commit.rb @@ -0,0 +1,82 @@ +require "grit" + +class GraphCommit + attr_accessor :time, :space + def initialize(commit) + @_commit = commit + @time = -1 + @space = 0 + end + + def method_missing(m, *args, &block) + @_commit.send(m, *args, &block) + end + + # Method is adding time and space on the + # list of commits. As well as returns date list + # corelated with time set on commits. + # + # @param [Array] comits to index + # + # @return [Array] list of commit dates corelated with time on commits + def self.index_commits(commits) + days, heads = [], [] + map = {} + + commits.reverse.each_with_index do |c,i| + c.time = i + days[i]=c.committed_date + map[c.id] = c + heads += c.refs unless c.refs.nil? + end + + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} + # sort heads so the master is top and current branches are closer + heads.sort! do |a,b| + if a.name == "master" + -1 + elsif b.name == "master" + 1 + else + b.commit.committed_date <=> a.commit.committed_date + end + end + + j = 0 + heads.each do |h| + if map.include? h.commit.id then + j = mark_chain(j+=1, map[h.commit.id], map) + end + end + days + end + + # Add space mark on commit and its parents + # + # @param [Fixnum] space (row on the graph) to be set + # @param [GraphCommit] the commit object. + # @param [Hash] map of commits + # + # @return [Fixnum] max space used. + def self.mark_chain(mark, commit, map) + commit.space = mark if commit.space == 0 + m1 = mark - 1 + marks = commit.parents.collect do |p| + if map.include? p.id and map[p.id].space == 0 then + mark_chain(m1+=1, map[p.id],map) + else + m1 + 1 + end + end + marks << mark + marks.compact.max + end + + def self.add_refs(commit, ref_cache) + if ref_cache.empty? + @repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref} + end + commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id + commit.refs ||= [] + end +end diff --git a/vendor/assets/javascripts/raphael.js b/vendor/assets/javascripts/raphael.js new file mode 100644 index 00000000000..d275d3435fb --- /dev/null +++ b/vendor/assets/javascripts/raphael.js @@ -0,0 +1,7 @@ +/* + * Raphael 1.5.2 - JavaScript Vector Library + * + * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://raphaeljs.com/license.html) license. + */ +(function(){function cC(a,b,c,d,e,f){function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(B(f)d)return d;while(cf?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cB(b){return function(c,d,e,f){var g={back:b};a.is(e,"function")?f=e:g.rot=e,c&&c.constructor==bN&&(c=c.attrs.path),c&&(g.along=c);return this.animate(g,d,f)}}function cp(){return this.x+q+this.y}function bm(a,b,c){function d(){var g=Array[e].slice.call(arguments,0),h=g[v]("►"),i=d.cache=d.cache||{},j=d.count=d.count||[];if(i[f](h))return c?c(i[h]):i[h];j[w]>=1e3&&delete i[j.shift()],j[L](h),i[h]=a[m](b,g);return c?c(i[h]):i[h]}return d}function bh(){var a=[],b=0;for(;b<32;b++)a[b]=(~~(y.random()*16))[H](16);a[12]=4,a[16]=(a[16]&3|8)[H](16);return"r-"+a[v]("")}function a(){if(a.is(arguments[0],G)){var b=arguments[0],d=bV[m](a,b.splice(0,3+a.is(b[0],E))),e=d.set();for(var g=0,h=b[w];g',bg=bf.firstChild,bg.style.behavior="url(#default#VML)";if(!bg||typeof bg.adj!="object")return a.type=null;bf=null}a.svg=!(a.vml=a.type=="VML"),j[e]=a[e],k=j[e],a._id=0,a._oid=0,a.fn={},a.is=function(a,b){b=x.call(b);if(b=="finite")return!O[f](+a);return b=="null"&&a===null||b==typeof a||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||J.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return((h<0)*180+y.atan(-i/-h)*180/D+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*D/180},a.deg=function(a){return a*180/D%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,G)){var e=b.length;while(e--)if(B(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(fb-d)return c-f+b}return c},a.setWindow=function(a){h=a,g=h.document};var bi=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write(""),e.close(),d=e.body}catch(f){d=createPopup().document.body}var h=d.createTextRange();bi=bm(function(a){try{d.style.color=r(a)[Y](c,p);var b=h.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b[H](16)).slice(-6)}catch(e){return"none"}})}else{var i=g.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",g.body[l](i),bi=bm(function(a){i.style.color=a;return g.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bi(b)},bj=function(){return"hsb("+[this.h,this.s,this.b]+")"},bk=function(){return"hsl("+[this.h,this.s,this.l]+")"},bl=function(){return this.hex};a.hsb2rgb=function(b,c,d,e){a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b&&(d=b.b,c=b.s,b=b.h,e=b.o);return a.hsl2rgb(b,c,d/2,e)},a.hsl2rgb=function(b,c,d,e){a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b&&(d=b.l,c=b.s,b=b.h);if(b>1||c>1||d>1)b/=360,c/=100,d/=100;var f={},g=["r","g","b"],h,i,j,k,l,m;if(!c)f={r:d,g:d,b:d};else{d<.5?h=d*(1+c):h=d+c-d*c,i=2*d-h;for(var n=0;n<3;n++)j=b+1/3*-(n-1),j<0&&j++,j>1&&j--,j*6<1?f[g[n]]=i+(h-i)*6*j:j*2<1?f[g[n]]=h:j*3<2?f[g[n]]=i+(h-i)*(2/3-j)*6:f[g[n]]=i}f.r*=255,f.g*=255,f.b*=255,f.hex="#"+(16777216|f.b|f.g<<8|f.r<<16).toString(16).slice(1),a.is(e,"finite")&&(f.opacity=e),f.toString=bl;return f},a.rgb2hsb=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;var f=z(b,c,d),g=A(b,c,d),h,i,j=f;if(g==f)return{h:0,s:0,b:f,toString:bj};var k=f-g;i=k/f,b==f?h=(c-d)/k:c==f?h=2+(d-b)/k:h=4+(b-c)/k,h/=6,h<0&&h++,h>1&&h--;return{h:h,s:i,b:j,toString:bj}},a.rgb2hsl=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;var f=z(b,c,d),g=A(b,c,d),h,i,j=(f+g)/2,k;if(g==f)k={h:0,s:0,l:j};else{var l=f-g;i=j<.5?l/(f+g):l/(2-f-g),b==f?h=(c-d)/l:c==f?h=2+(d-b)/l:h=4+(b-c)/l,h/=6,h<0&&h++,h>1&&h--,k={h:h,s:i,l:j}}k.toString=bk;return k},a._path2string=function(){return this.join(",")[Y](ba,"$1")},a.getRGB=bm(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none"};!_[f](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bi(b));var c,d,e,g,h,i,j,k=b.match(N);if(k){k[2]&&(g=T(k[2].substring(5),16),e=T(k[2].substring(3,5),16),d=T(k[2].substring(1,3),16)),k[3]&&(g=T((i=k[3].charAt(3))+i,16),e=T((i=k[3].charAt(2))+i,16),d=T((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s]($),d=S(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=S(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),g=S(j[2]),j[2].slice(-1)=="%"&&(g*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=S(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s]($),d=S(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=S(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),g=S(j[2]),j[2].slice(-1)=="%"&&(g*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=S(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,g,h)}if(k[6]){j=k[6][s]($),d=S(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=S(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),g=S(j[2]),j[2].slice(-1)=="%"&&(g*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=S(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,g,h)}k={r:d,g:e,b:g},k.hex="#"+(16777216|g|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1}},a),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=bm(function(b){if(!b)return null;var c={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},d=[];a.is(b,G)&&a.is(b[0],G)&&(d=bo(b)),d[w]||r(b)[Y](bb,function(a,b,e){var f=[],g=x.call(b);e[Y](bc,function(a,b){b&&f[L](+b)}),g=="m"&&f[w]>2&&(d[L]([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");while(f[w]>=c[g]){d[L]([b][n](f.splice(0,c[g])));if(!c[g])break}}),d[H]=a._path2string;return d}),a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,l=C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h,m=a+2*i*(c-a)+i*i*(e-2*c+a),n=b+2*i*(d-b)+i*i*(f-2*d+b),o=c+2*i*(e-c)+i*i*(g-2*e+c),p=d+2*i*(f-d)+i*i*(h-2*f+d),q=(1-i)*a+i*c,r=(1-i)*b+i*d,s=(1-i)*e+i*g,t=(1-i)*f+i*h,u=90-y.atan((m-o)/(n-p))*180/D;(m>o||n1&&(x=y.sqrt(x),c=x*c,d=x*d);var z=c*c,A=d*d,C=(f==g?-1:1)*y.sqrt(B((z*A-z*u*u-A*t*t)/(z*u*u+A*t*t))),E=C*c*u/d+(a+h)/2,F=C*-d*t/c+(b+i)/2,G=y.asin(((b-F)/d).toFixed(9)),H=y.asin(((i-F)/d).toFixed(9));G=aH&&(G=G-D*2),!g&&H>G&&(H=H-D*2)}else G=j[0],H=j[1],E=j[2],F=j[3];var I=H-G;if(B(I)>k){var J=H,K=h,L=i;H=G+k*(g&&H>G?1:-1),h=E+c*y.cos(H),i=F+d*y.sin(H),m=bt(h,i,c,d,e,0,g,K,L,[H,J,E,F])}I=H-G;var M=y.cos(G),N=y.sin(G),O=y.cos(H),P=y.sin(H),Q=y.tan(I/4),R=4/3*c*Q,S=4/3*d*Q,T=[a,b],U=[a+R*N,b-S*M],V=[h+R*P,i-S*O],W=[h,i];U[0]=2*T[0]-U[0],U[1]=2*T[1]-U[1];if(j)return[U,V,W][n](m);m=[U,V,W][n](m)[v]()[s](",");var X=[];for(var Y=0,Z=m[w];Y"1e12"&&(l=.5),B(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bu(a,b,c,d,e,f,g,h,l),p[L](q.x),o[L](q.y)),n>0&&n<1&&(q=bu(a,b,c,d,e,f,g,h,n),p[L](q.x),o[L](q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+y.sqrt(j*j-4*i*k))/2/i,n=(-j-y.sqrt(j*j-4*i*k))/2/i,B(l)>"1e12"&&(l=.5),B(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bu(a,b,c,d,e,f,g,h,l),p[L](q.x),o[L](q.y)),n>0&&n<1&&(q=bu(a,b,c,d,e,f,g,h,n),p[L](q.x),o[L](q.y));return{min:{x:A[m](0,p),y:A[m](0,o)},max:{x:z[m](0,p),y:z[m](0,o)}}}),bw=bm(function(a,b){var c=bq(a),d=b&&bq(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bt[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bs(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bs(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](br(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](br(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](br(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](br(b.x,b.y,b.X,b.Y))}return a},h=function(a,b){if(a[b][w]>7){a[b].shift();var e=a[b];while(e[w])a.splice(b++,0,["C"][n](e.splice(0,6)));a.splice(b,1),k=z(c[w],d&&d[w]||0)}},i=function(a,b,e,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),e.bx=0,e.by=0,e.x=a[g][1],e.y=a[g][2],k=z(c[w],d&&d[w]||0))};for(var j=0,k=z(c[w],d&&d[w]||0);j.5)*2-1;C(e-.5,2)+C(f-.5,2)>.25&&(f=y.sqrt(.25-C(e-.5,2))*g+.5)&&f!=.5&&(f=f.toFixed(5)-1e-5*g)}return p}),b=b[s](/\s*\-\s*/);if(d=="linear"){var i=b.shift();i=-S(i);if(isNaN(i))return null;var j=[0,0,y.cos(i*D/180),y.sin(i*D/180)],k=1/(z(B(j[2]),B(j[3]))||1);j[2]*=k,j[3]*=k,j[2]<0&&(j[0]=-j[2],j[2]=0),j[3]<0&&(j[1]=-j[3],j[3]=0)}var m=bx(b);if(!m)return null;var n=a.getAttribute(I);n=n.match(/^url\(#(.*)\)$/),n&&c.defs.removeChild(g.getElementById(n[1]));var o=bG(d+"Gradient");o.id=bh(),bG(o,d=="radial"?{fx:e,fy:f}:{x1:j[0],y1:j[1],x2:j[2],y2:j[3]}),c.defs[l](o);for(var q=0,t=m[w];q1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(o),h[R](n,G.hex),n=="stroke"&&G[f]("opacity")&&bG(h,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity});break;case"gradient":(({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!="r")&&bI(h,o,c.paper);break;case"opacity":i.gradient&&!i[f]("stroke-opacity")&&bG(h,{"stroke-opacity":o>1?o/100:o});case"fill-opacity":if(i.gradient){var H=g.getElementById(h.getAttribute(I)[Y](/^url\(#|\)$/g,p));if(H){var J=H.getElementsByTagName("stop");J[J[w]-1][R]("stop-opacity",o)}break};default:n=="font-size"&&(o=T(o,10)+"px");var K=n[Y](/(\-.)/g,function(a){return V.call(a.substring(1))});h.style[K]=o,h[R](n,o)}}bM(c,d),m?c.rotate(m.join(q)):S(j)&&c.rotate(j,!0)},bL=1.2,bM=function(b,c){if(b.type=="text"&&!!(c[f]("text")||c[f]("font")||c[f]("font-size")||c[f]("x")||c[f]("y"))){var d=b.attrs,e=b.node,h=e.firstChild?T(g.defaultView.getComputedStyle(e.firstChild,p).getPropertyValue("font-size"),10):10;if(c[f]("text")){d.text=c.text;while(e.firstChild)e.removeChild(e.firstChild);var i=r(c.text)[s]("\n");for(var j=0,k=i[w];jb.height&&(b.height=f.y+f.height-b.y),f.x+f.width-b.x>b.width&&(b.width=f.x+f.width-b.x)}}a&&this.hide();return b},bN[e].attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate()),(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale()),d.gradient&&d.fill=="none"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,F)){if(b=="translation")return cA.call(this);if(b=="rotation")return this.rotate();if(b=="scale")return this.scale();if(b==I&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(c==null&&a.is(b,G)){var g={};for(var h=0,i=b.length;h")),m.W=h.w=m.paper.span.offsetWidth,m.H=h.h=m.paper.span.offsetHeight,m.X=h.x,m.Y=h.y+Q(m.H/2);switch(h["text-anchor"]){case"start":m.node.style["v-text-align"]="left",m.bbx=Q(m.W/2);break;case"end":m.node.style["v-text-align"]="right",m.bbx=-Q(m.W/2);break;default:m.node.style["v-text-align"]="center"}}},bI=function(a,b){a.attrs=a.attrs||{};var c=a.attrs,d,e="linear",f=".5 .5";a.attrs.gradient=b,b=r(b)[Y](bd,function(a,b,c){e="radial",b&&c&&(b=S(b),c=S(c),C(b-.5,2)+C(c-.5,2)>.25&&(c=y.sqrt(.25-C(b-.5,2))*((c>.5)*2-1)+.5),f=b+q+c);return p}),b=b[s](/\s*\-\s*/);if(e=="linear"){var g=b.shift();g=-S(g);if(isNaN(g))return null}var h=bx(b);if(!h)return null;a=a.shape||a.node,d=a.getElementsByTagName(I)[0]||cd(I),!d.parentNode&&a.appendChild(d);if(h[w]){d.on=!0,d.method="none",d.color=h[0].color,d.color2=h[h[w]-1].color;var i=[];for(var j=0,k=h[w];j')}}catch(ce){cd=function(a){return g.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}bV=function(){var b=by[m](0,arguments),c=b.container,d=b.height,e,f=b.width,h=b.x,i=b.y;if(!c)throw new Error("VML container not found.");var k=new j,n=k.canvas=g.createElement("div"),o=n.style;h=h||0,i=i||0,f=f||512,d=d||342,f==+f&&(f+="px"),d==+d&&(d+="px"),k.width=1e3,k.height=1e3,k.coordsize=b_*1e3+q+b_*1e3,k.coordorigin="0 0",k.span=g.createElement("span"),k.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",n[l](k.span),o.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(g.body[l](n),o.left=h+"px",o.top=i+"px",o.position="absolute"):c.firstChild?c.insertBefore(n,c.firstChild):c[l](n),bz.call(k,k,a.fn);return k},k.clear=function(){this.canvas.innerHTML=p,this.span=g.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas[l](this.span),this.bottom=this.top=null},k.remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a);return!0}}var cf=navigator.userAgent.match(/Version\/(.*?)\s/);navigator.vendor=="Apple Computer, Inc."&&(cf&&cf[1]<4||navigator.platform.slice(0,2)=="iP")?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});h.setTimeout(function(){a.remove()})}:k.safari=function(){};var cg=function(){this.returnValue=!1},ch=function(){return this.originalEvent.preventDefault()},ci=function(){this.cancelBubble=!0},cj=function(){return this.originalEvent.stopPropagation()},ck=function(){if(g.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,g=function(e){if(o&&u[f](b))for(var g=0,h=e.targetTouches&&e.targetTouches.length;g1&&(a=Array[e].splice.call(arguments,0,arguments[w]));return new cD(a)},k.setSize=bU,k.top=k.bottom=null,k.raphael=a,bO.resetScale=function(){if(this.removed)return this;this._.sx=1,this._.sy=1,this.attrs.scale="1 1"},bO.scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:cp};b=b||a,!+b&&(b=a);var e,f,g,h,i=this.attrs;if(a!=0){var j=this.getBBox(),k=j.x+j.width/2,l=j.y+j.height/2,m=B(a/this._.sx),o=B(b/this._.sy);c=+c||c==0?c:k,d=+d||d==0?d:l;var r=this._.sx>0,s=this._.sy>0,t=~~(a/B(a)),u=~~(b/B(b)),x=m*t,y=o*u,z=this.node.style,A=c+B(k-c)*x*(k>c==r?1:-1),C=d+B(l-d)*y*(l>d==s?1:-1),D=a*t>b*u?o:m;switch(this.type){case"rect":case"image":var E=i.width*m,F=i.height*o;this.attr({height:F,r:i.r*D,width:E,x:A-E/2,y:C-F/2});break;case"circle":case"ellipse":this.attr({rx:i.rx*m,ry:i.ry*o,r:i.r*D,cx:A,cy:C});break;case"text":this.attr({x:A,y:C});break;case"path":var G=bp(i.path),H=!0,I=r?x:m,J=s?y:o;for(var K=0,L=G[w];Kr?p=n.data[r*l]:(p=a.findDotsAtSegment(b,c,d,e,f,g,h,i,r/l),n.data[r]=p),r&&(k+=C(C(o.x-p.x,2)+C(o.y-p.y,2),.5));if(j!=null&&k>=j)return p;o=p}if(j==null)return k},cs=function(b,c){return function(d,e,f){d=bw(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;oe){if(c&&!l.start){m=cr(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C",m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M",m.x,m.y+"C",m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]][v](),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cr(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[1],i[2],i[3],i[4],i[5],i[6],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},ct=cs(1),cu=cs(),cv=cs(0,1);bO.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return ct(this.attrs.path)}},bO.getPointAtLength=function(a){if(this.type=="path")return cu(this.attrs.path,a)},bO.getSubpath=function(a,b){if(this.type=="path"){if(B(this.getTotalLength()-b)<"1e-6")return cv(this.attrs.path,a).end;var c=cv(this.attrs.path,b,1);return a?cv(c,a).end:c}},a.easing_formulas={linear:function(a){return a},"<":function(a){return C(a,3)},">":function(a){return C(a-1,3)+1},"<>":function(a){a=a*2;if(a<1)return C(a,3)/2;a-=2;return(C(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==0||a==1)return a;var b=.3,c=b/4;return C(2,-10*a)*y.sin((a-c)*2*D/b)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};var cw=[],cx=function(){var b=+(new Date);for(var c=0;c Date: Sun, 13 Nov 2011 13:58:45 +0200 Subject: Graph: base implementation --- app/assets/stylesheets/projects.css.scss | 11 +++++++- app/controllers/projects_controller.rb | 4 +-- app/views/layouts/project.html.haml | 2 +- app/views/projects/graph.html.haml | 9 ++++++ lib/assets/javascripts/branch-graph.js | 47 ++++++++++++++++++-------------- 5 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 app/views/projects/graph.html.haml diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index b03ad1059e0..9c0eb591bdb 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -468,4 +468,13 @@ body.project-page table .commit { /** UI autocomplete **/ .ui-autocomplete { @include round-borders-all(5px); } .ui-menu-item { cursor: pointer } - + +#holder { + border: solid 1px #999; + cursor: move; + height: 70%; + overflow: scroll; + position: absolute; + width: auto; + margin: 6ex 3ex 0ex 0ex; +} diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a0e727954d8..0564a82b0fa 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -150,11 +150,9 @@ class ProjectsController < ApplicationController h[:id] = c.sha h[:date] = c.date h[:message] = c.message.force_encoding("UTF-8") - h[:email] = c.author.email + h[:login] = c.author.email h end.to_json - - render :text => @commits_json end def blob diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index ed289707af3..fc28b9307b3 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -22,7 +22,7 @@ = link_to "History", project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil = link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil = link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil - = link_to "Network graph", graph_project_path(@project), :class => current_page?(:controller => "projects", :action => "graph", :project_id => @project) ? "current" : nil + = link_to "Network graph", graph_project_path(@project), :class => current_page?(:controller => "projects", :action => "graph", :id => @project) ? "current" : nil = link_to team_project_path(@project), :class => (current_page?(:controller => "projects", :action => "team", :id => @project) || controller.controller_name == "team_members") ? "current" : nil do Team - if @project.users_projects.count > 0 diff --git a/app/views/projects/graph.html.haml b/app/views/projects/graph.html.haml new file mode 100644 index 00000000000..b5f6921d407 --- /dev/null +++ b/app/views/projects/graph.html.haml @@ -0,0 +1,9 @@ +#holder.graph + +:javascript + var chunk1={commits:#{@commits_json}}; + var days=#{@days_json}; + initGraph(); + $(function(){ + branchGraph($("#holder")[0]); + }); diff --git a/lib/assets/javascripts/branch-graph.js b/lib/assets/javascripts/branch-graph.js index b98c836ff5c..f8b73660d11 100644 --- a/lib/assets/javascripts/branch-graph.js +++ b/lib/assets/javascripts/branch-graph.js @@ -1,30 +1,37 @@ -var commits = chunk1.commits, +var commits = {}, comms = {}, pixelsX = [], pixelsY = [], mmax = Math.max, mtime = 0, mspace = 0, - parents = {}; -for (var i = 0, ii = commits.length; i < ii; i++) { - for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { - parents[commits[i].parents[j][0]] = true; - } - mtime = Math.max(mtime, commits[i].time); - mspace = Math.max(mspace, commits[i].space); -} -mtime = mtime + 4; -mspace = mspace + 10; -for (i = 0; i < ii; i++) { - if (commits[i].id in parents) { - commits[i].isParent = true; - } - comms[commits[i].id] = commits[i]; -} -var colors = ["#000"]; -for (var k = 0; k < mspace; k++) { - colors.push(Raphael.getColor()); + parents = {}, + ii = 0, + colors = ["#000"]; + +function initGraph(){ + commits = chunk1.commits; + ii = commits.length; + for (var i = 0; i < ii; i++) { + for (var j = 0, jj = commits[i].parents.length; j < jj; j++) { + parents[commits[i].parents[j][0]] = true; + } + mtime = Math.max(mtime, commits[i].time); + mspace = Math.max(mspace, commits[i].space); + } + mtime = mtime + 4; + mspace = mspace + 10; + for (i = 0; i < ii; i++) { + if (commits[i].id in parents) { + commits[i].isParent = true; + } + comms[commits[i].id] = commits[i]; + } + for (var k = 0; k < mspace; k++) { + colors.push(Raphael.getColor()); + } } + function branchGraph(holder) { var ch = mspace * 20 + 20, cw = mtime * 20 + 20, r = Raphael("holder", cw, ch), -- cgit v1.2.1 From c89682605ff36b9c7659ae204ebd08c7c9c7951c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sun, 13 Nov 2011 15:12:52 +0200 Subject: Graph: is done. v1 --- app/assets/stylesheets/projects.css.scss | 5 +---- lib/assets/javascripts/branch-graph.js | 9 +++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index 9c0eb591bdb..84aa1716ac1 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -473,8 +473,5 @@ body.project-page table .commit { border: solid 1px #999; cursor: move; height: 70%; - overflow: scroll; - position: absolute; - width: auto; - margin: 6ex 3ex 0ex 0ex; + overflow: hidden; } diff --git a/lib/assets/javascripts/branch-graph.js b/lib/assets/javascripts/branch-graph.js index f8b73660d11..8c45aefe688 100644 --- a/lib/assets/javascripts/branch-graph.js +++ b/lib/assets/javascripts/branch-graph.js @@ -37,16 +37,17 @@ function branchGraph(holder) { r = Raphael("holder", cw, ch), top = r.set(); var cuday = 0, cumonth = ""; - r.rect(0,0,days.length*20+20,40).attr({fill: "#999"}); + r.rect(0, 0, days.length * 20 + 20, 20).attr({fill: "#474D57"}); + r.rect(0, 20, days.length * 20 + 20, 20).attr({fill: "#f7f7f7"}); for (mm = 0; mm < days.length; mm++) { if(days[mm] != null){ if(cuday != days[mm][0]){ - r.text(10+mm*20,30,days[mm][0]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); + r.text(10 + mm * 20, 30, days[mm][0]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); cuday = days[mm][0] } if(cumonth != days[mm][1]){ - r.text(10+mm*20,10,days[mm][1]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); + r.text(10 + mm * 20, 10, days[mm][1]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"}); cumonth = days[mm][1] } @@ -62,7 +63,7 @@ function branchGraph(holder) { if (shortrefs.length > 15){ shortrefs = shortrefs.substr(0,13) + "..."; } - var t = r.text(x+5,y+5,shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666", + var t = r.text(x+5, y+5, shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666", title: longrefs, cursor: "pointer", rotation: "90"}); var textbox = t.getBBox(); -- cgit v1.2.1 From dc844f013848ee1fa7721173bda3f3fc0ed0b8ba Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sun, 13 Nov 2011 15:31:18 +0200 Subject: Graph: small refactoring --- app/controllers/projects_controller.rb | 5 ++++- lib/graph_commit.rb | 11 ++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0564a82b0fa..7f662a43773 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -181,7 +181,10 @@ class ProjectsController < ApplicationController def add_refs(commit, ref_cache) if ref_cache.empty? - @repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref} + @repo.refs.each do |ref| + ref_cache[ref.commit.id] ||= [] + ref_cache[ref.commit.id] << ref + end end commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id commit.refs ||= [] diff --git a/lib/graph_commit.rb b/lib/graph_commit.rb index ee765cb4ef2..477f4f422e0 100644 --- a/lib/graph_commit.rb +++ b/lib/graph_commit.rb @@ -25,7 +25,7 @@ class GraphCommit commits.reverse.each_with_index do |c,i| c.time = i - days[i]=c.committed_date + days[i] = c.committed_date map[c.id] = c heads += c.refs unless c.refs.nil? end @@ -63,7 +63,7 @@ class GraphCommit m1 = mark - 1 marks = commit.parents.collect do |p| if map.include? p.id and map[p.id].space == 0 then - mark_chain(m1+=1, map[p.id],map) + mark_chain(m1 += 1, map[p.id],map) else m1 + 1 end @@ -72,11 +72,4 @@ class GraphCommit marks.compact.max end - def self.add_refs(commit, ref_cache) - if ref_cache.empty? - @repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref} - end - commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id - commit.refs ||= [] - end end -- cgit v1.2.1