Commit 7a998266 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge branch 'master' of https://gitlab.com/gitlab-org/gitlab-ce into remove-grack-lfs

parents 71952d05 551ffc0a
CHANGELOG merge=union
*.js.es6 gitlab-language=javascript
......@@ -28,6 +28,7 @@ stages:
- prepare
- test
- post-test
- pages
# Prepare and merge knapsack tests
.knapsack-state: &knapsack-state
......@@ -40,6 +41,7 @@ stages:
paths:
- knapsack/
artifacts:
expire_in: 31d
paths:
- knapsack/
......@@ -81,8 +83,10 @@ update-knapsack:
- cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH}
- knapsack rspec
artifacts:
expire_in: 31d
paths:
- knapsack/
- coverage/
.spinach-knapsack: &spinach-knapsack
stage: test
......@@ -97,8 +101,10 @@ update-knapsack:
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts:
expire_in: 31d
paths:
- knapsack/
- coverage/
rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack
......@@ -186,14 +192,14 @@ spinach 9 10 ruby23: *spinach-knapsack-ruby23
# Other generic tests
.static-analyses-variables: &static-analyses-variables
.ruby-static-analysis: &ruby-static-analysis
variables:
SIMPLECOV: "false"
USE_DB: "false"
USE_BUNDLE_INSTALL: "true"
.exec: &exec
<<: *static-analyses-variables
<<: *ruby-static-analysis
stage: test
script:
- bundle exec $CI_BUILD_NAME
......@@ -220,16 +226,35 @@ teaspoon:
bundler:audit:
stage: test
<<: *static-analyses-variables
<<: *ruby-static-analysis
only:
- master
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
coverage:
stage: post-test
services: []
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "true"
script:
- bundle exec scripts/merge-simplecov
artifacts:
name: coverage
expire_in: 31d
paths:
- coverage/index.html
- coverage/assets/
# Notify slack in the end
notify:slack:
stage: post-test
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure
......@@ -238,3 +263,18 @@ notify:slack:
- tags@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- tags@gitlab-org/gitlab-ee
pages:
before_script: []
stage: pages
dependencies:
- coverage
script:
- mv public/ .public/
- mkdir public/
- mv coverage public/coverage-ruby
artifacts:
paths:
- public
only:
- master
......@@ -149,19 +149,19 @@ Style/EmptyLinesAroundAccessModifier:
# Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around class bodies.
Style/EmptyLinesAroundClassBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around module bodies.
Style/EmptyLinesAroundModuleBody:
Enabled: false
Enabled: true
# Keeps track of empty lines around method bodies.
Style/EmptyLinesAroundMethodBody:
Enabled: false
Enabled: true
# Avoid the use of END blocks.
Style/EndBlock:
......@@ -373,6 +373,10 @@ Style/SpaceAfterNot:
Style/SpaceAfterSemicolon:
Enabled: true
# Use space around equals in parameter default
Style/SpaceAroundEqualsInParameterDefault:
Enabled: true
# Use a space around keywords if appropriate.
Style/SpaceAroundKeyword:
Enabled: true
......@@ -510,6 +514,15 @@ Metrics/PerceivedComplexity:
#################### Lint ################################
# Checks for useless access modifiers.
Lint/UselessAccessModifier:
Enabled: true
# Checks for attempts to use `private` or `protected` to set the visibility
# of a class method, which does not work.
Lint/IneffectiveAccessModifier:
Enabled: false
# Checks for ambiguous operators in the first argument of a method invocation
# without parentheses.
Lint/AmbiguousOperator:
......
......@@ -19,10 +19,6 @@ Lint/AssignmentInCondition:
Lint/HandleExceptions:
Enabled: false
# Offense count: 21
Lint/IneffectiveAccessModifier:
Enabled: false
# Offense count: 2
Lint/Loop:
Enabled: false
......@@ -48,10 +44,6 @@ Lint/UnusedBlockArgument:
Lint/UnusedMethodArgument:
Enabled: false
# Offense count: 11
Lint/UselessAccessModifier:
Enabled: false
# Offense count: 12
# Cop supports --auto-correct.
Performance/PushSplat:
......@@ -347,13 +339,6 @@ Style/SingleLineBlockParams:
Style/SingleLineMethods:
Enabled: false
# Offense count: 14
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
# Offense count: 119
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
......
# .simplecov
SimpleCov.start 'rails' do
merge_timeout 3600
end
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Improve diff performance by eliminating redundant checks for text blobs
- Convert switch icon into icon font (ClemMakesApps)
- API: Endpoints for enabling and disabling deploy keys
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps)
- Cache highlighted diff lines for merge requests
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes
- Add "No one can push" as an option for protected branches. !5081
- Improve performance of AutolinkFilter#text_parse by using XPath
- Environments have an url to link to
- Update `timeago` plugin to use multiple string/locale settings
- Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check
- Clean up unused routes (Josef Strzibny)
- Fix issue on empty project to allow developers to only push to protected branches if given permission
- Add green outline to New Branch button. !5447 (winniehell)
- Optimize generating of cache keys for issues and notes
- Improve performance of syntax highlighting Markdown code blocks
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- Remove delay when hitting "Reply..." button on page with a lot of discussions
- Retrieve rendered HTML from cache in one request
- Fix renaming repository when name contains invalid chararacters under project settings
- Fix devise deprecation warnings.
- Update version_sorter and use new interface for faster tag sorting
- Optimize checking if a user has read access to a list of issues !5370
- Nokogiri's various parsing methods are now instrumented
- Add simple identifier to public SSH keys (muteor)
- Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
- Add a way to send an email and create an issue based on private personal token. Find the email address from issues page. !3363
- Fix filter input alignment (ClemMakesApps)
- Include old revision in merge request update hooks (Ben Boeckel)
- Add build event color in HipChat messages (David Eisner)
- Make fork counter always clickable. !5463 (winniehell)
- Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- The overhead of instrumented method calls has been reduced
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell)
- Check for Ci::Build artifacts at database level on pipeline partial
- Convert image diff background image to CSS (ClemMakesApps)
- Remove unnecessary index_projects_on_builds_enabled index from the projects table
- Make "New issue" button in Issue page less obtrusive !5457 (winniehell)
- Gitlab::Metrics.current_transaction needs to be public for RailsQueueDuration
- Fix search for notes which belongs to deleted objects
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Allow branch names ending with .json for graph and network page !5579 (winniehell)
- Add the `sprockets-es6` gem
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed
- Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
- Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
- Add commit stats in commit api. !5517 (dixpac)
- Add CI configuration button on project page
- Make error pages responsive (Takuya Noguchi)
- Fix skip_repo parameter being ignored when destroying a namespace
- Change requests_profiles resource constraint to catch virtually any file
- Bump gitlab_git to lazy load compare commits
- Reduce number of queries made for merge_requests/:id/diffs
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix RequestProfiler::Middleware error when code is reloaded in development
- Catch what warden might throw when profiling requests to re-throw it
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- Adds support for pending invitation project members importing projects
- Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
v 8.10.5 (unreleased)
v 8.10.4
- Don't close referenced upstream issues from a forked project.
- Fixes issue with dropdowns `enter` key not working correctly. !5544
- Fix Import/Export project import not working in HA mode. !5618
- Fix Import/Export error checking versions. !5638
v 8.10.3
- Fix Import/Export issue importing milestones and labels not associated properly. !5426
- Fix timing problems running imports on production. !5523
- Add a log message when a project is scheduled for destruction for debugging. !5540
- Fix hooks missing on imported GitLab projects. !5549
- Properly abort a merge when merge conflicts occur. !5569
- Fix importer for GitHub Pull Requests when a branch was removed. !5573
- Ignore invalid IPs in X-Forwarded-For when trusted proxies are configured. !5584
- Trim extra displayed carriage returns in diffs and files with CRLFs. !5588
v 8.10.2
- User can now search branches by name. !5144
- Page is now properly rendered after committing the first file and creating the first branch. !5399
- Add branch or tag icon to ref in builds page. !5434
- Fix backup restore. !5459
- Use project ID in repository cache to prevent stale data from persisting across projects. !5460
- Fix issue with autocomplete search not working with enter key. !5466
- Add iid to MR API response. !5468
- Disable MySQL foreign key checks before dropping all tables. !5472
- Ensure relative paths for video are rewritten as we do for images. !5474
- Ensure current user can retry a build before showing the 'Retry' button. !5476
- Add ENV variable to skip repository storages validations. !5478
- Added `*.js.es6 gitlab-language=javascript` to `.gitattributes`. !5486
- Don't show comment button in gutter of diffs on MR discussion tab. !5493
- Rescue Rugged::OSError (lock exists) when creating references. !5497
- Fix expand all diffs button in compare view. !5500
- Show release notes in tags list. !5503
- Fix a bug where forking a project from a repository storage to another would fail. !5509
- Fix missing schema update for `20160722221922`. !5512
- Update `gitlab-shell` version to 3.2.1 in the 8.9->8.10 update guide. !5516
v 8.10.1
- Refactor repository storages documentation. !5428
- Gracefully handle case when keep-around references are corrupted or exist already. !5430
- Add detailed info on storage path mountpoints. !5437
- Fix Error 500 when creating Wiki pages with hyphens or spaces. !5444
- Fix bug where replies to commit notes displayed in the MR discussion tab wouldn't show up on the commit page. !5446
- Ignore invalid trusted proxies in X-Forwarded-For header. !5454
- Add links to the real markdown.md file for all GFM examples. !5458
v 8.10.0
- Fix profile activity heatmap to show correct day name (eanplatter)
......@@ -52,6 +178,9 @@ v 8.10.0
- Fix check for New Branch button on Issue page. !4630 (winniehell)
- Fix GFM autocomplete not working on wiki pages
- Fixed enter key not triggering click on first row when searching in a dropdown
- Updated dropdowns in issuable form to use new GitLab dropdown style
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags. !5105 (redetection)
......@@ -82,7 +211,6 @@ v 8.10.0
- API: Todos. !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups. !5050 (Robert Schilling)
- API: Expose `developers_can_push` and `developers_can_merge` for branches. !5208 (Robert Schilling)
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- Add "Enabled Git access protocols" to Application Settings
- Diffs will create button/diff form on demand no on server side
- Reduce size of HTML used by diff comment forms
......
......@@ -41,6 +41,8 @@ abbreviation.
If you have read this guide and want to know how the GitLab [core team]
operates please see [the GitLab contributing process](PROCESS.md).
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
## Contributor license agreement
By submitting code as an individual you agree to the
......@@ -334,6 +336,10 @@ request is as follows:
1. If your code creates new files on disk please read the
[shared files guidelines](doc/development/shared_files.md).
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
1. If your merge request adds one or more migrations, make sure to execute all
migrations on a fresh database before the MR is reviewed. If the review leads
to large changes in the MR, do this again once the review is complete.
1. For more complex migrations, write tests.
The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get
......@@ -459,8 +465,10 @@ merge request:
- multi-line method chaining style **Option B**: dot `.` on previous line
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
1. [Testing](doc/development/testing.md)
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security
......@@ -530,6 +538,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
......
......@@ -9,6 +9,7 @@ gem 'responders', '~> 2.0'
# Specify a sprockets version due to increased performance
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/6069
gem 'sprockets', '~> 3.6.0'
gem 'sprockets-es6'
# Default values for AR models
gem 'default_value_for', '~> 3.0.0'
......@@ -52,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.4.1'
gem 'gitlab_git', '~> 10.4.5'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -153,7 +154,7 @@ gem 'settingslogic', '~> 2.0.9'
# Misc
gem 'version_sorter', '~> 2.0.0'
gem 'version_sorter', '~> 2.1.0'
# Cache
gem 'redis-rails', '~> 4.0.0'
......@@ -224,7 +225,7 @@ gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.6.1'
gem 'gemojione', '~> 3.0'
gem 'gon', '~> 6.0.1'
gem 'gon', '~> 6.1.0'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
gem 'jquery-ui-rails', '~> 5.0.0'
......@@ -252,7 +253,7 @@ group :development do
gem 'letter_opener_web', '~> 1.3.0'
gem 'rerun', '~> 0.11.0'
gem 'bullet', '~> 5.0.0', require: false
gem 'bullet', '~> 5.2.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0'
......@@ -274,7 +275,7 @@ group :development, :test do
gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0'
gem 'database_cleaner', '~> 1.4.0'
gem 'database_cleaner', '~> 1.5.0'
gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry', '~> 0.4.5'
......@@ -302,7 +303,7 @@ group :development, :test do
gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false
gem 'simplecov', '0.12.0', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
......@@ -325,7 +326,7 @@ group :production do
gem 'gitlab_meta', '7.0'
end
gem 'newrelic_rpm', '~> 3.14'
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
......@@ -333,6 +334,8 @@ gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8'
gem 'ruby-prof', '~> 0.15.9'
## CI
gem 'activerecord-session_store', '~> 1.0.0'
gem 'nested_form', '~> 0.3.2'
......
......@@ -59,7 +59,7 @@ GEM
oauth2 (~> 1.0)
asciidoctor (1.5.3)
ast (2.3.0)
attr_encrypted (3.0.1)
attr_encrypted (3.0.3)
encryptor (~> 3.0.0)
attr_required (1.0.0)
autoprefixer-rails (6.2.3)
......@@ -85,6 +85,10 @@ GEM
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.11)
......@@ -100,9 +104,9 @@ GEM
brakeman (3.3.2)
browser (2.2.0)
builder (3.2.2)
bullet (5.0.0)
bullet (5.2.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
uniform_notifier (~> 1.10.0)
bundler-audit (0.5.0)
bundler (~> 1.2)
thor (~> 0.18)
......@@ -149,11 +153,11 @@ GEM
d3_rails (3.5.11)
railties (>= 3.1.0)
daemons (1.2.3)
database_cleaner (1.4.1)
database_cleaner (1.5.3)
debug_inspector (0.0.2)
debugger-ruby_core_source (1.3.8)
default_value_for (3.0.1)
activerecord (>= 3.2.0, < 5.0)
default_value_for (3.0.2)
activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
devise (4.1.1)
......@@ -274,7 +278,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.4.1)
gitlab_git (10.4.5)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -299,7 +303,7 @@ GEM
gollum-rugged_adapter (0.4.2)
mime-types (>= 1.15)
rugged (~> 0.24.0, >= 0.21.3)
gon (6.0.1)
gon (6.1.0)
actionpack (>= 3.0)
json
multi_json
......@@ -400,7 +404,7 @@ GEM
nested_form (0.3.2)
net-ldap (0.12.1)
net-ssh (3.0.1)
newrelic_rpm (3.14.1.311)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
......@@ -505,7 +509,7 @@ GEM
rack-cors (0.4.0)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-oauth2 (1.2.1)
rack-oauth2 (1.2.3)
activesupport (>= 2.3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
......@@ -571,7 +575,7 @@ GEM
redis-store (~> 1.1.0)
redis-store (1.1.7)
redis (>= 2.2)
request_store (1.3.0)
request_store (1.3.1)
rerun (0.11.0)
listen (~> 3.0)
responders (2.1.1)
......@@ -616,6 +620,7 @@ GEM
rubocop (>= 0.40.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.15.9)
ruby-progressbar (1.8.1)
ruby-saml (1.3.0)
nokogiri (>= 1.5.10)
......@@ -668,9 +673,9 @@ GEM
rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0)
simple_oauth (0.1.9)
simplecov (0.11.2)
simplecov (0.12.0)
docile (~> 1.1.0)
json (~> 1.8)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
sinatra (1.4.7)
......@@ -700,6 +705,10 @@ GEM
sprockets (3.6.3)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-es6 (0.9.0)
babel-source (>= 5.8.11)
babel-transpiler
sprockets (>= 3.0.0)
sprockets-rails (3.1.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
......@@ -766,10 +775,10 @@ GEM
unicorn-worker-killer (0.4.4)
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.9.0)
uniform_notifier (1.10.0)
uuid (2.3.8)
macaddr (~> 1.0)
version_sorter (2.0.0)
version_sorter (2.1.0)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
......@@ -821,7 +830,7 @@ DEPENDENCIES
bootstrap-sass (~> 3.3.0)
brakeman (~> 3.3.0)
browser (~> 2.2)
bullet (~> 5.0.0)
bullet (~> 5.2.0)
bundler-audit (~> 0.5.0)
byebug (~> 8.2.1)
capybara (~> 2.6.2)
......@@ -833,7 +842,7 @@ DEPENDENCIES
connection_pool (~> 2.0)
creole (~> 0.5.0)
d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0)
database_cleaner (~> 1.5.0)
default_value_for (~> 3.0.0)
devise (~> 4.0)
devise-two-factor (~> 3.0.0)
......@@ -861,12 +870,12 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.4.1)
gitlab_git (~> 10.4.5)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1)
gon (~> 6.1.0)
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
hamlit (~> 2.5)
......@@ -893,7 +902,7 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14)
newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
......@@ -940,6 +949,7 @@ DEPENDENCIES
rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9)
sanitize (~> 2.0)
sass-rails (~> 5.0.0)
scss_lint (~> 0.47.0)
......@@ -952,7 +962,7 @@ DEPENDENCIES
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.0)
sidekiq-cron (~> 0.4.0)
simplecov (~> 0.11.0)
simplecov (= 0.12.0)
sinatra (~> 1.4.4)
six (~> 0.2.0)
slack-notifier (~> 1.2.0)
......@@ -963,6 +973,7 @@ DEPENDENCIES
spring-commands-spinach (~> 1.1.0)
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.6.0)
sprockets-es6
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
task_list (~> 1.0.2)
......@@ -978,7 +989,7 @@ DEPENDENCIES
unf (~> 0.1.4)
unicorn (~> 4.9.0)
unicorn-worker-killer (~> 0.4.2)
version_sorter (~> 2.0.0)
version_sorter (~> 2.1.0)
virtus (~> 1.0.1)
vmstat (~> 2.1.1)
web-console (~> 2.0)
......
......@@ -8,6 +8,8 @@ treatment, etc.). And so that maintainers know what to expect from contributors
(use the latest version, ensure that the issue is addressed, friendly treatment,
etc.).
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
## Common actions
### Issue team
......
(function() {
this.LabelManager = (function() {
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
function LabelManager(opts) {
var ref, ref1, ref2;
if (opts == null) {
opts = {};
}
this.togglePriorityButton = (ref = opts.togglePriorityButton) != null ? ref : $('.js-toggle-priority'), this.prioritizedLabels = (ref1 = opts.prioritizedLabels) != null ? ref1 : $('.js-prioritized-labels'), this.otherLabels = (ref2 = opts.otherLabels) != null ? ref2 : $('.js-other-labels');
this.prioritizedLabels.sortable({
items: 'li',
placeholder: 'list-placeholder',
axis: 'y',
update: this.onPrioritySortUpdate.bind(this)
});
this.bindEvents();
}
LabelManager.prototype.bindEvents = function() {
return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
};
LabelManager.prototype.onTogglePriorityClick = function(e) {
var $btn, $label, $tooltip, _this, action;
e.preventDefault();
_this = e.data;
$btn = $(e.currentTarget);
$label = $("#" + ($btn.data('domId')));
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
$tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action);
};
LabelManager.prototype.toggleLabelPriority = function($label, action, persistState) {
var $from, $target, _this, url, xhr;
if (persistState == null) {
persistState = true;
}
_this = this;
url = $label.find('.js-toggle-priority').data('url');
$target = this.prioritizedLabels;
$from = this.otherLabels;
if (action === 'remove') {
$target = this.otherLabels;
$from = this.prioritizedLabels;
}
if ($from.find('li').length === 1) {
$from.find('.empty-message').removeClass('hidden');
}
if (!$target.find('li').length) {
$target.find('.empty-message').addClass('hidden');
}
$label.detach().appendTo($target);
if (!persistState) {
return;
}
if (action === 'remove') {
xhr = $.ajax({
url: url,
type: 'DELETE'
});
if (!$from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
} else {
xhr = this.savePrioritySort($label, action);
}
return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action));
};
LabelManager.prototype.onPrioritySortUpdate = function() {
var xhr;
xhr = this.savePrioritySort();
return xhr.fail(function() {
return new Flash(this.errorMessage, 'alert');
});
};
LabelManager.prototype.savePrioritySort = function() {
return $.post({
url: this.prioritizedLabels.data('url'),
data: {
label_ids: this.getSortedLabelsIds()
}
});
};
LabelManager.prototype.rollbackLabelPosition = function($label, originalAction) {
var action;
action = originalAction === 'remove' ? 'add' : 'remove';
this.toggleLabelPriority($label, action, false);
return new Flash(this.errorMessage, 'alert');
};
LabelManager.prototype.getSortedLabelsIds = function() {
var sortedIds;
sortedIds = [];
this.prioritizedLabels.find('li').each(function() {
return sortedIds.push($(this).data('id'));
});
return sortedIds;
};
return LabelManager;
})();
}).call(this);
class @LabelManager
errorMessage: 'Unable to update label prioritization at this time'
constructor: (opts = {}) ->
# Defaults
{
@togglePriorityButton = $('.js-toggle-priority')
@prioritizedLabels = $('.js-prioritized-labels')
@otherLabels = $('.js-other-labels')
} = opts
@prioritizedLabels.sortable(
items: 'li'
placeholder: 'list-placeholder'
axis: 'y'
update: @onPrioritySortUpdate.bind(@)
)
@bindEvents()
bindEvents: ->
@togglePriorityButton.on 'click', @, @onTogglePriorityClick
onTogglePriorityClick: (e) ->
e.preventDefault()
_this = e.data
$btn = $(e.currentTarget)
$label = $("##{$btn.data('domId')}")
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
# Make sure tooltip will hide
$tooltip = $ "##{$btn.find('.has-tooltip:visible').attr('aria-describedby')}"
$tooltip.tooltip 'destroy'
_this.toggleLabelPriority($label, action)
toggleLabelPriority: ($label, action, persistState = true) ->
_this = @
url = $label.find('.js-toggle-priority').data 'url'
$target = @prioritizedLabels
$from = @otherLabels
# Optimistic update
if action is 'remove'
$target = @otherLabels
$from = @prioritizedLabels
if $from.find('li').length is 1
$from.find('.empty-message').removeClass('hidden')
if not $target.find('li').length
$target.find('.empty-message').addClass('hidden')
$label.detach().appendTo($target)
# Return if we are not persisting state
return unless persistState
if action is 'remove'
xhr = $.ajax url: url, type: 'DELETE'
# Restore empty message
$from.find('.empty-message').removeClass('hidden') unless $from.find('li').length
else
xhr = @savePrioritySort($label, action)
xhr.fail @rollbackLabelPosition.bind(@, $label, action)
onPrioritySortUpdate: ->
xhr = @savePrioritySort()
xhr.fail ->
new Flash(@errorMessage, 'alert')
savePrioritySort: () ->
$.post
url: @prioritizedLabels.data('url')
data:
label_ids: @getSortedLabelsIds()
rollbackLabelPosition: ($label, originalAction)->
action = if originalAction is 'remove' then 'add' else 'remove'
@toggleLabelPriority($label, action, false)
new Flash(@errorMessage, 'alert')
getSortedLabelsIds: ->
sortedIds = []
@prioritizedLabels.find('li').each ->
sortedIds.push $(@).data 'id'
sortedIds
(function() {
this.Activities = (function() {
function Activities() {
Pager.init(20, true, false, this.updateTooltips);
$(".event-filter-link").on("click", (function(_this) {
return function(event) {
event.preventDefault();
_this.toggleFilter($(event.currentTarget));
return _this.reloadActivities();
};
})(this));
}
Activities.prototype.updateTooltips = function() {
return gl.utils.localTimeAgo($('.js-timeago', '#activity'));
};
Activities.prototype.reloadActivities = function() {
$(".content_list").html('');
return Pager.init(20, true);
};
Activities.prototype.toggleFilter = function(sender) {
var event_filters, filter;
$('.event-filter .active').removeClass("active");
event_filters = $.cookie("event_filter");
filter = sender.attr("id").split("_")[0];
$.cookie("event_filter", (event_filters !== filter ? filter : ""), {
path: '/'
});
if (event_filters !== filter) {
return sender.closest('li').toggleClass("active");
}
};
return Activities;
})();
}).call(this);
class @Activities
constructor: ->
Pager.init 20, true, false, @updateTooltips
$(".event-filter-link").on "click", (event) =>
event.preventDefault()
@toggleFilter($(event.currentTarget))
@reloadActivities()
updateTooltips: ->
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
reloadActivities: ->
$(".content_list").html ''
Pager.init 20, true
toggleFilter: (sender) ->
$('.event-filter .active').removeClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
$.cookie "event_filter", (if event_filters isnt filter then filter else ""), { path: '/' }
if event_filters isnt filter
sender.closest('li').toggleClass "active"
(function() {
this.Admin = (function() {
function Admin() {
var modal, showBlacklistType;
$('input#user_force_random_password').on('change', function(elem) {
var elems;
elems = $('#user_password, #user_password_confirmation');
if ($(this).attr('checked')) {
return elems.val('').attr('disabled', true);
} else {
return elems.removeAttr('disabled');
}
});
$('body').on('click', '.js-toggle-colors-link', function(e) {
e.preventDefault();
return $('.js-toggle-colors-container').toggle();
});
$('.log-tabs a').click(function(e) {
e.preventDefault();
return $(this).tab('show');
});
$('.log-bottom').click(function(e) {
var visible_log;
e.preventDefault();
visible_log = $(".file-content:visible");
return visible_log.animate({
scrollTop: visible_log.find('ol').height()
}, "fast");
});
modal = $('.change-owner-holder');
$('.change-owner-link').bind("click", function(e) {
e.preventDefault();
$(this).hide();
return modal.show();
});
$('.change-owner-cancel-link').bind("click", function(e) {
e.preventDefault();
modal.hide();
return $('.change-owner-link').show();
});
$('li.project_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
});
$('li.group_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
});
showBlacklistType = function() {
if ($("input[name='blacklist_type']:checked").val() === 'file') {
$('.blacklist-file').show();
return $('.blacklist-raw').hide();
} else {
$('.blacklist-file').hide();
return $('.blacklist-raw').show();
}
};
$("input[name='blacklist_type']").click(showBlacklistType);
showBlacklistType();
}
return Admin;
})();
}).call(this);
class @Admin
constructor: ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
if $(@).attr 'checked'
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
$('body').on 'click', '.js-toggle-colors-link', (e) ->
e.preventDefault()
$('.js-toggle-colors-container').toggle()
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
$('.log-bottom').click (e) ->
e.preventDefault()
visible_log = $(".file-content:visible")
visible_log.animate({ scrollTop: visible_log.find('ol').height() }, "fast")
modal = $('.change-owner-holder')
$('.change-owner-link').bind "click", (e) ->
e.preventDefault()
$(this).hide()
modal.show()
$('.change-owner-cancel-link').bind "click", (e) ->
e.preventDefault()
modal.hide()
$('.change-owner-link').show()
$('li.project_member').bind 'ajax:success', ->
Turbolinks.visit(location.href)
$('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href)
showBlacklistType = ->
if $("input[name='blacklist_type']:checked").val() == 'file'
$('.blacklist-file').show()
$('.blacklist-raw').hide()
else
$('.blacklist-file').hide()
$('.blacklist-raw').show()
$("input[name='blacklist_type']").click showBlacklistType
showBlacklistType()
(function() {
this.Api = {
groupsPath: "/api/:version/groups.json",
groupPath: "/api/:version/groups/:id.json",
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
labelsPath: "/api/:version/projects/:id/labels",
licensePath: "/api/:version/licenses/:key",
gitignorePath: "/api/:version/gitignores/:key",
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key",
group: function(group_id, callback) {
var url;
url = Api.buildUrl(Api.groupPath);
url = url.replace(':id', group_id);
return $.ajax({
url: url,
data: {
private_token: gon.api_token
},
dataType: "json"
}).done(function(group) {
return callback(group);
});
},
groups: function(query, skip_ldap, callback) {
var url;
url = Api.buildUrl(Api.groupsPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(groups) {
return callback(groups);
});
},
namespaces: function(query, callback) {
var url;
url = Api.buildUrl(Api.namespacesPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(namespaces) {
return callback(namespaces);
});
},
projects: function(query, order, callback) {
var url;
url = Api.buildUrl(Api.projectsPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
order_by: order,
per_page: 20
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
},
newLabel: function(project_id, data, callback) {
var url;
url = Api.buildUrl(Api.labelsPath);
url = url.replace(':id', project_id);
data.private_token = gon.api_token;
return $.ajax({
url: url,
type: "POST",
data: data,
dataType: "json"
}).done(function(label) {
return callback(label);
}).error(function(message) {
return callback(message.responseJSON);
});
},
groupProjects: function(group_id, query, callback) {
var url;
url = Api.buildUrl(Api.groupProjectsPath);
url = url.replace(':id', group_id);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
},
licenseText: function(key, data, callback) {
var url;
url = Api.buildUrl(Api.licensePath).replace(':key', key);
return $.ajax({
url: url,
data: data
}).done(function(license) {
return callback(license);
});
},
gitignoreText: function(key, callback) {
var url;
url = Api.buildUrl(Api.gitignorePath).replace(':key', key);
return $.get(url, function(gitignore) {
return callback(gitignore);
});
},
gitlabCiYml: function(key, callback) {
var url;
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key);
return $.get(url, function(file) {
return callback(file);
});
},
buildUrl: function(url) {
if (gon.relative_url_root != null) {
url = gon.relative_url_root + url;
}
return url.replace(':version', gon.api_version);
}
};
}).call(this);
@Api =
groupsPath: "/api/:version/groups.json"
groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json"
projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
group: (group_id, callback) ->
url = Api.buildUrl(Api.groupPath)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (group) ->
callback(group)
# Return groups list. Filtered by query
# Only active groups retrieved
groups: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.groupsPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (groups) ->
callback(groups)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespacesPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (namespaces) ->
callback(namespaces)
# Return projects list. Filtered by query
projects: (query, order, callback) ->
url = Api.buildUrl(Api.projectsPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
order_by: order
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
newLabel: (project_id, data, callback) ->
url = Api.buildUrl(Api.labelsPath)
url = url.replace(':id', project_id)
data.private_token = gon.api_token
$.ajax(
url: url
type: "POST"
data: data
dataType: "json"
).done (label) ->
callback(label)
.error (message) ->
callback(message.responseJSON)
# Return group projects list. Filtered by query
groupProjects: (group_id, query, callback) ->
url = Api.buildUrl(Api.groupProjectsPath)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
# Return text for a specific license
licenseText: (key, data, callback) ->
url = Api.buildUrl(Api.licensePath).replace(':key', key)
$.ajax(
url: url
data: data
).done (license) ->
callback(license)
gitignoreText: (key, callback) ->
url = Api.buildUrl(Api.gitignorePath).replace(':key', key)
$.get url, (gitignore) ->
callback(gitignore)
gitlabCiYml: (key, callback) ->
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
$.get url, (file) ->
callback(file)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
This diff is collapsed.
# 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 jquery2
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight
#= require jquery-ui/sortable
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
#= require jquery.highlight
#= require jquery.waitforimages
#= require jquery.atwho
#= require jquery.scrollTo
#= require jquery.turbolinks
#= require turbolinks
#= require autosave
#= require bootstrap/affix
#= require bootstrap/alert
#= require bootstrap/button
#= require bootstrap/collapse
#= require bootstrap/dropdown
#= require bootstrap/modal
#= require bootstrap/scrollspy
#= require bootstrap/tab
#= require bootstrap/transition
#= require bootstrap/tooltip
#= require bootstrap/popover
#= require select2
#= require ace/ace
#= require ace/ext-searchbox
#= require underscore
#= require dropzone
#= require mousetrap
#= require mousetrap/pause
#= require shortcuts
#= require shortcuts_navigation
#= require shortcuts_dashboard_navigation
#= require shortcuts_issuable
#= require shortcuts_network
#= require jquery.nicescroll
#= require date.format
#= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
window.split = (val) ->
return val.split( /,\s*/ )
window.extractLast = (term) ->
return split( term ).pop()
window.rstrip = (val) ->
return if val then val.replace(/\s+$/, '') else val
# Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) ->
field = $(field_selector)
closest_submit = field.closest('form').find(button_selector)
closest_submit.disable() if rstrip(field.val()) is ""
field.on 'input', ->
if rstrip($(@).val()) is ""
closest_submit.disable()
else
closest_submit.enable()
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
updateButtons = ->
filled = true
form.find('input').filter(form_selector).each ->
filled = rstrip($(this).val()) != "" || !$(this).attr('required')
if filled
closest_submit.enable()
else
closest_submit.disable()
updateButtons()
form.keyup(updateButtons)
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
window.unbindEvents = ->
$(document).off('scroll')
window.shiftWindow = ->
scrollBy 0, -100
document.addEventListener("page:fetch", unbindEvents)
window.addEventListener "hashchange", shiftWindow
window.onload = ->
# Scroll the window to avoid the topnav bar
# https://github.com/twitter/bootstrap/issues/1768
if location.hash
setTimeout shiftWindow, 100
$ ->
$document = $(document)
$window = $(window)
$body = $('body')
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
$(".nav-sidebar").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
# Click a .js-select-on-focus field, select the contents
$(".js-select-on-focus").on "focusin", ->
# Prevent a mouseup event from deselecting the input
$(this).select().one 'mouseup', (e) ->
e.preventDefault()
$('.remove-row').bind 'ajax:success', ->
$(this).closest('li').fadeOut()
$('.js-remove-tr').bind 'ajax:before', ->
$(this).hide()
$('.js-remove-tr').bind 'ajax:success', ->
$(this).closest('tr').fadeOut()
# Initialize select2 selects
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
# Close select2 on escape
$('.js-select2').bind 'select2-close', ->
setTimeout ( ->
$('.select2-container-active').removeClass('select2-container-active')
$(':focus').blur()
), 1
# Initialize tooltips
$body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
$el.data('placement') || 'bottom'
)
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true)
# Flash
if (flash = $(".flash-container")).length > 0
flash.click -> $(@).fadeOut()
flash.show()
# Disable form buttons while a form is submitting
$body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
when 'ajax:beforeSend', 'submit'
buttons.disable()
else
buttons.enable()
$(document).ajaxError (e, xhrObj, xhrSetting, xhrErrorText) ->
if xhrObj.status is 401
new Flash 'You need to be logged in.', 'alert'
else if xhrObj.status in [ 404, 500 ]
new Flash 'Something went wrong on our end.', 'alert'
# Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
$document.on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active')
# Show/hide comments on diff
$body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
$document.off "click", '.js-confirm-danger'
$document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault()
btn = $(e.target)
text = btn.data("confirm-danger-message")
form = btn.closest("form")
new ConfirmDangerModal(form, text)
$document.on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
$this = $(this)
$this.attr 'value', $this.val()
return
$document
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
$this.attr 'value', $this.val()
$sidebarGutterToggle = $('.js-sidebar-toggle')
$document
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
$gutterIcon = $sidebarGutterToggle.find('i')
if $gutterIcon.hasClass('fa-angle-double-right')
$sidebarGutterToggle.trigger('click')
fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm"
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
$window
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
checkInitialSidebarSize()
new Aside()
# Sidenav pinning
if $window.width() < 1024 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false', { path: '/', expires: 365 * 10 })
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
$document
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
$pinBtn = $(e.currentTarget)
$page = $ '.page-with-sidebar'
$topNav = $ '.navbar-fixed-top'
$tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
doPinNav = not $page.is('.page-sidebar-pinned')
tooltipText = 'Pin navigation'
$(this).toggleClass 'is-active'
if doPinNav
$page.addClass('page-sidebar-pinned')
$topNav.addClass('header-pinned-nav')
else
$tooltip.remove() # Remove it immediately when collapsing the sidebar
$page.removeClass('page-sidebar-pinned')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$topNav.removeClass('header-pinned-nav')
.toggleClass('header-collapsed header-expanded')
# Save settings
$.cookie 'pin_nav', doPinNav, { path: '/', expires: 365 * 10 }
if $.cookie('pin_nav') is 'true' or doPinNav
tooltipText = 'Unpin navigation'
# Update tooltip text immediately
$tooltip.find('.tooltip-inner').text(tooltipText)
# Persist tooltip title
$pinBtn.attr('title', tooltipText).tooltip('fixTitle')
(function() {
this.Aside = (function() {
function Aside() {
$(document).off("click", "a.show-aside");
$(document).on("click", 'a.show-aside', function(e) {
var btn, icon;
e.preventDefault();
btn = $(e.currentTarget);
icon = btn.find('i');
if (icon.hasClass('fa-angle-left')) {
btn.parent().find('section').hide();
btn.parent().find('aside').fadeIn();
return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
} else {
btn.parent().find('aside').hide();
btn.parent().find('section').fadeIn();
return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
}
});
}
return Aside;
})();
}).call(this);
class @Aside
constructor: ->
$(document).off "click", "a.show-aside"
$(document).on "click", 'a.show-aside', (e) ->
e.preventDefault()
btn = $(e.currentTarget)
icon = btn.find('i')
if icon.hasClass('fa-angle-left')
btn.parent().find('section').hide()
btn.parent().find('aside').fadeIn()
icon.removeClass('fa-angle-left').addClass('fa-angle-right')
else
btn.parent().find('aside').hide()
btn.parent().find('section').fadeIn()
icon.removeClass('fa-angle-right').addClass('fa-angle-left')
(function() {
this.Autosave = (function() {
function Autosave(field, key) {
this.field = field;
if (key.join != null) {
key = key.join("/");
}
this.key = "autosave/" + key;
this.field.data("autosave", this);
this.restore();
this.field.on("input", (function(_this) {
return function() {
return _this.save();
};
})(this));
}
Autosave.prototype.restore = function() {
var e, error, text;
if (window.localStorage == null) {
return;
}
try {
text = window.localStorage.getItem(this.key);
} catch (error) {
e = error;
return;
}
if ((text != null ? text.length : void 0) > 0) {
this.field.val(text);
}
return this.field.trigger("input");
};
Autosave.prototype.save = function() {
var text;
if (window.localStorage == null) {
return;
}
text = this.field.val();
if ((text != null ? text.length : void 0) > 0) {
try {
return window.localStorage.setItem(this.key, text);
} catch (undefined) {}
} else {
return this.reset();
}
};
Autosave.prototype.reset = function() {
if (window.localStorage == null) {
return;
}
try {
return window.localStorage.removeItem(this.key);
} catch (undefined) {}
};
return Autosave;
})();
}).call(this);
class @Autosave
constructor: (field, key) ->
@field = field
key = key.join("/") if key.join?
@key = "autosave/#{key}"
@field.data "autosave", this
@restore()
@field.on "input", => @save()
restore: ->
return unless window.localStorage?
try
text = window.localStorage.getItem @key
catch e
return
@field.val text if text?.length > 0
@field.trigger "input"
save: ->
return unless window.localStorage?
text = @field.val()
if text?.length > 0
try
window.localStorage.setItem @key, text
else
@reset()
reset: ->
return unless window.localStorage?
try
window.localStorage.removeItem @key
This diff is collapsed.
This diff is collapsed.
/*= require jquery.ba-resize */
/*= require autosize */
(function() {
$(function() {
var $fields;
$fields = $('.js-autosize');
$fields.on('autosize:resized', function() {
var $field;
$field = $(this);
return $field.data('height', $field.outerHeight());
});
$fields.on('resize.autosize', function() {
var $field;
$field = $(this);
if ($field.data('height') !== $field.outerHeight()) {
$field.data('height', $field.outerHeight());
autosize.destroy($field);
return $field.css('max-height', window.outerHeight);
}
});
autosize($fields);
autosize.update($fields);
return $fields.css('resize', 'vertical');
});
}).call(this);
#= require jquery.ba-resize
#= require autosize
$ ->
$fields = $('.js-autosize')
$fields.on 'autosize:resized', ->
$field = $(@)
$field.data('height', $field.outerHeight())
$fields.on 'resize.autosize', ->
$field = $(@)
if $field.data('height') != $field.outerHeight()
$field.data('height', $field.outerHeight())
autosize.destroy($field)
$field.css('max-height', window.outerHeight)
autosize($fields)
autosize.update($fields)
$fields.css('resize', 'vertical')
$ ->
$("body").on "click", ".js-details-target", ->
container = $(@).closest(".js-details-container")
container.toggleClass("open")
# Show details content. Hides link after click.
#
# %div
# %a.js-details-expand
# %div.js-details-content
#
$("body").on "click", ".js-details-expand", (e) ->
$(@).next('.js-details-content').removeClass("hide")
$(@).hide()
e.preventDefault()
(function() {
$(function() {
$("body").on("click", ".js-details-target", function() {
var container;
container = $(this).closest(".js-details-container");
return container.toggleClass("open");
});
return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide");
$(this).hide();
return e.preventDefault();
});
});
}).call(this);
/*= require extensions/jquery */
(function() {
var isMac, keyCodeIs;
isMac = function() {
return navigator.userAgent.match(/Macintosh/);
};
keyCodeIs = function(e, keyCode) {
if ((e.originalEvent && e.originalEvent.repeat) || e.repeat) {
return false;
}
return e.keyCode === keyCode;
};
$(document).on('keydown.quick_submit', '.js-quick-submit', function(e) {
var $form, $submit_button;
if (!keyCodeIs(e, 13)) {
return;
}
if (!((e.metaKey && !e.altKey && !e.ctrlKey && !e.shiftKey) || (e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey))) {
return;
}
e.preventDefault();
$form = $(e.target).closest('form');
$submit_button = $form.find('input[type=submit], button[type=submit]');
if ($submit_button.attr('disabled')) {
return;
}
$submit_button.disable();
return $form.submit();
});
$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function(e) {
var $this, title;
if (!keyCodeIs(e, 9)) {
return;
}
if (isMac()) {
title = "You can also press &#8984;-Enter";
} else {
title = "You can also press Ctrl-Enter";
}
$this = $(this);
return $this.tooltip({
container: 'body',
html: 'true',
placement: 'auto top',
title: title,
trigger: 'manual'
}).tooltip('show').one('blur', function() {
return $this.tooltip('hide');
});
});
}).call(this);
# Quick Submit behavior
#
# When a child field of a form with a `js-quick-submit` class receives a
# "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
# is submitted.
#
#= require extensions/jquery
#
# ### Example Markup
#
# <form action="/foo" class="js-quick-submit">
# <input type="text" />
# <textarea></textarea>
# <input type="submit" value="Submit" />
# </form>
#
isMac = ->
navigator.userAgent.match(/Macintosh/)
keyCodeIs = (e, keyCode) ->
return false if (e.originalEvent && e.originalEvent.repeat) || e.repeat
return e.keyCode == keyCode
$(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
return unless keyCodeIs(e, 13) # Enter
return unless (e.metaKey && !e.altKey && !e.ctrlKey && !e.shiftKey) || (e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey)
e.preventDefault()
$form = $(e.target).closest('form')
$submit_button = $form.find('input[type=submit], button[type=submit]')
return if $submit_button.attr('disabled')
$submit_button.disable()
$form.submit()
# If the user tabs to a submit button on a `js-quick-submit` form, display a
# tooltip to let them know they could've used the hotkey
$(document).on 'keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', (e) ->
return unless keyCodeIs(e, 9) # Tab
if isMac()
title = "You can also press &#8984;-Enter"
else
title = "You can also press Ctrl-Enter"
$this = $(@)
$this.tooltip(
container: 'body'
html: 'true'
placement: 'auto top'
title: title
trigger: 'manual'
).tooltip('show').one('blur', -> $this.tooltip('hide'))
/*= require extensions/jquery */
(function() {
$.fn.requiresInput = function() {
var $button, $form, fieldSelector, requireInput, required;
$form = $(this);
$button = $('button[type=submit], input[type=submit]', $form);
required = '[required=required]';
fieldSelector = "input" + required + ", select" + required + ", textarea" + required;
requireInput = function() {
var values;
values = _.map($(fieldSelector, $form), function(field) {
return field.value;
});
if (values.length && _.any(values, _.isEmpty)) {
return $button.disable();
} else {
return $button.enable();
}
};
requireInput();
return $form.on('change input', fieldSelector, requireInput);
};
$(function() {
var $form, hideOrShowHelpBlock;
$form = $('form.js-requires-input');
$form.requiresInput();
hideOrShowHelpBlock = function(form) {
var selected;
selected = $('.js-select-namespace option:selected');
if (selected.length && selected.data('options-parent') === 'groups') {
return form.find('.help-block').hide();
} else if (selected.length) {
return form.find('.help-block').show();
}
};
hideOrShowHelpBlock($form);
return $('.select2.js-select-namespace').change(function() {
return hideOrShowHelpBlock($form);
});
});
}).call(this);
# Requires Input behavior
#
# When called on a form with input fields with the `required` attribute, the
# form's submit button will be disabled until all required fields have values.
#
#= require extensions/jquery
#
# ### Example Markup
#
# <form class="js-requires-input">
# <input type="text" required="required">
# <input type="submit" value="Submit">
# </form>
#
$.fn.requiresInput = ->
$form = $(this)
$button = $('button[type=submit], input[type=submit]', $form)
required = '[required=required]'
fieldSelector = "input#{required}, select#{required}, textarea#{required}"
requireInput = ->
# Collect the input values of *all* required fields
values = _.map $(fieldSelector, $form), (field) -> field.value
# Disable the button if any required fields are empty
if values.length && _.any(values, _.isEmpty)
$button.disable()
else
$button.enable()
# Set initial button state
requireInput()
$form.on 'change input', fieldSelector, requireInput
$ ->
$form = $('form.js-requires-input')
$form.requiresInput()
# Hide or Show the help block when creating a new project
# based on the option selected
hideOrShowHelpBlock = (form) ->
selected = $('.js-select-namespace option:selected')
if selected.length and selected.data('options-parent') is 'groups'
return form.find('.help-block').hide()
else if selected.length
form.find('.help-block').show()
hideOrShowHelpBlock($form)
$('.select2.js-select-namespace').change -> hideOrShowHelpBlock($form)
$ ->
# Toggle button. Show/hide content inside parent container.
# Button does not change visibility. If button has icon - it changes chevron style.
#
# %div.js-toggle-container
# %a.js-toggle-button
# %div.js-toggle-content
#
$("body").on "click", ".js-toggle-button", (e) ->
$(@).find('i').
toggleClass('fa fa-chevron-down').
toggleClass('fa fa-chevron-up')
$(@).closest(".js-toggle-container").find(".js-toggle-content").toggle()
e.preventDefault()
(function() {
$(function() {
return $("body").on("click", ".js-toggle-button", function(e) {
$(this).find('i').toggleClass('fa fa-chevron-down').toggleClass('fa fa-chevron-up');
$(this).closest(".js-toggle-container").find(".js-toggle-content").toggle();
return e.preventDefault();
});
});
}).call(this);
/*= require blob/template_selector */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.BlobCiYamlSelector = (function(superClass) {
extend(BlobCiYamlSelector, superClass);
function BlobCiYamlSelector() {
return BlobCiYamlSelector.__super__.constructor.apply(this, arguments);
}
BlobCiYamlSelector.prototype.requestFile = function(query) {
return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
};
return BlobCiYamlSelector;
})(TemplateSelector);
this.BlobCiYamlSelectors = (function() {
function BlobCiYamlSelectors(opts) {
var ref;
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-gitlab-ci-yml-selector'), this.editor = opts.editor;
this.$dropdowns.each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return new BlobCiYamlSelector({
pattern: /(.gitlab-ci.yml)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
dropdown: $dropdown,
editor: _this.editor
});
};
})(this));
}
return BlobCiYamlSelectors;
})();
}).call(this);
#= require blob/template_selector
class @BlobCiYamlSelector extends TemplateSelector
requestFile: (query) ->
Api.gitlabCiYml query.name, @requestFileSuccess.bind(@)
class @BlobCiYamlSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-gitlab-ci-yml-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobCiYamlSelector(
pattern: /(.gitlab-ci.yml)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
(function() {
this.BlobFileDropzone = (function() {
function BlobFileDropzone(form, method) {
var dropzone, form_dropzone, submitButton;
form_dropzone = form.find('.dropzone');
Dropzone.autoDiscover = false;
dropzone = form_dropzone.dropzone({
autoDiscover: false,
autoProcessQueue: false,
url: form.attr('action'),
method: method,
clickable: true,
uploadMultiple: false,
paramName: "file",
maxFilesize: gon.max_file_size || 10,
parallelUploads: 1,
maxFiles: 1,
addRemoveLinks: true,
previewsContainer: '.dropzone-previews',
headers: {
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
},
init: function() {
this.on('addedfile', function(file) {
$('.dropzone-alerts').html('').hide();
});
this.on('success', function(header, response) {
window.location.href = response.filePath;
});
this.on('maxfilesexceeded', function(file) {
this.removeFile(file);
});
return this.on('sending', function(file, xhr, formData) {
formData.append('target_branch', form.find('.js-target-branch').val());
formData.append('create_merge_request', form.find('.js-create-merge-request').val());
formData.append('commit_message', form.find('.js-commit-message').val());
});
},
error: function(file, errorMessage) {
var stripped;
stripped = $("<div/>").html(errorMessage).text();
$('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show();
this.removeFile(file);
}
});
submitButton = form.find('#submit-all')[0];
submitButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
alert("Please select a file");
}
dropzone[0].dropzone.processQueue();
return false;
});
}
return BlobFileDropzone;
})();
}).call(this);
class @BlobFileDropzone
constructor: (form, method) ->
form_dropzone = form.find('.dropzone')
Dropzone.autoDiscover = false
dropzone = form_dropzone.dropzone(
autoDiscover: false
autoProcessQueue: false
url: form.attr('action')
# Rails uses a hidden input field for PUT
# http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
method: method
clickable: true
uploadMultiple: false
paramName: "file"
maxFilesize: gon.max_file_size or 10
parallelUploads: 1
maxFiles: 1
addRemoveLinks: true
previewsContainer: '.dropzone-previews'
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
init: ->
this.on 'addedfile', (file) ->
$('.dropzone-alerts').html('').hide()
return
this.on 'success', (header, response) ->
window.location.href = response.filePath
return
this.on 'maxfilesexceeded', (file) ->
@removeFile file
return
this.on 'sending', (file, xhr, formData) ->
formData.append('target_branch', form.find('.js-target-branch').val())
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
formData.append('commit_message', form.find('.js-commit-message').val())
return
# Override behavior of adding error underneath preview
error: (file, errorMessage) ->
stripped = $("<div/>").html(errorMessage).text();
$('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show()
@removeFile file
return
)
submitButton = form.find('#submit-all')[0]
submitButton.addEventListener 'click', (e) ->
e.preventDefault()
e.stopPropagation()
alert "Please select a file" if dropzone[0].dropzone.getQueuedFiles().length == 0
dropzone[0].dropzone.processQueue()
return false
/*= require blob/template_selector */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.BlobGitignoreSelector = (function(superClass) {
extend(BlobGitignoreSelector, superClass);
function BlobGitignoreSelector() {
return BlobGitignoreSelector.__super__.constructor.apply(this, arguments);
}
BlobGitignoreSelector.prototype.requestFile = function(query) {
return Api.gitignoreText(query.name, this.requestFileSuccess.bind(this));
};
return BlobGitignoreSelector;
})(TemplateSelector);
}).call(this);
#= require blob/template_selector
class @BlobGitignoreSelector extends TemplateSelector
requestFile: (query) ->
Api.gitignoreText query.name, @requestFileSuccess.bind(@)
(function() {
this.BlobGitignoreSelectors = (function() {
function BlobGitignoreSelectors(opts) {
var ref;
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-gitignore-selector'), this.editor = opts.editor;
this.$dropdowns.each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return new BlobGitignoreSelector({
pattern: /(.gitignore)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
dropdown: $dropdown,
editor: _this.editor
});
};
})(this));
}
return BlobGitignoreSelectors;
})();
}).call(this);
class @BlobGitignoreSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-gitignore-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobGitignoreSelector(
pattern: /(.gitignore)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
/*= require blob/template_selector */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.BlobLicenseSelector = (function(superClass) {
extend(BlobLicenseSelector, superClass);
function BlobLicenseSelector() {
return BlobLicenseSelector.__super__.constructor.apply(this, arguments);
}
BlobLicenseSelector.prototype.requestFile = function(query) {
var data;
data = {
project: this.dropdown.data('project'),
fullname: this.dropdown.data('fullname')
};
return Api.licenseText(query.id, data, this.requestFileSuccess.bind(this));
};
return BlobLicenseSelector;
})(TemplateSelector);
}).call(this);
#= require blob/template_selector
class @BlobLicenseSelector extends TemplateSelector
requestFile: (query) ->
data =
project: @dropdown.data('project')
fullname: @dropdown.data('fullname')
Api.licenseText query.id, data, @requestFileSuccess.bind(@)
(function() {
this.BlobLicenseSelectors = (function() {
function BlobLicenseSelectors(opts) {
var ref;
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-license-selector'), this.editor = opts.editor;
this.$dropdowns.each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return new BlobLicenseSelector({
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-license-selector-wrap'),
dropdown: $dropdown,
editor: _this.editor
});
};
})(this));
}
return BlobLicenseSelectors;
})();
}).call(this);
class @BlobLicenseSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-license-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobLicenseSelector(
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-license-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.EditBlob = (function() {
function EditBlob(assets_path, ace_mode) {
if (ace_mode == null) {
ace_mode = null;
}
this.editModeLinkClickHandler = bind(this.editModeLinkClickHandler, this);
ace.config.set("modePath", assets_path + "/ace");
ace.config.loadModule("ace/ext/searchbox");
this.editor = ace.edit("editor");
this.editor.focus();
if (ace_mode) {
this.editor.getSession().setMode("ace/mode/" + ace_mode);
}
$('form').submit((function(_this) {
return function() {
return $("#file-content").val(_this.editor.getValue());
};
})(this));
this.initModePanesAndLinks();
new BlobLicenseSelectors({
editor: this.editor
});
new BlobGitignoreSelectors({
editor: this.editor
});
new BlobCiYamlSelectors({
editor: this.editor
});
}
EditBlob.prototype.initModePanesAndLinks = function() {
this.$editModePanes = $(".js-edit-mode-pane");
this.$editModeLinks = $(".js-edit-mode a");
return this.$editModeLinks.click(this.editModeLinkClickHandler);
};
EditBlob.prototype.editModeLinkClickHandler = function(event) {
var currentLink, currentPane, paneId;
event.preventDefault();
currentLink = $(event.target);
paneId = currentLink.attr("href");
currentPane = this.$editModePanes.filter(paneId);
this.$editModeLinks.parent().removeClass("active hover");
currentLink.parent().addClass("active hover");
this.$editModePanes.hide();
currentPane.fadeIn(200);
if (paneId === "#preview") {
return $.post(currentLink.data("preview-url"), {
content: this.editor.getValue()
}, function(response) {
currentPane.empty().append(response);
return currentPane.syntaxHighlight();
});
} else {
return this.editor.focus();
}
};
return EditBlob;
})();
}).call(this);
class @EditBlob
constructor: (assets_path, ace_mode = null) ->
ace.config.set "modePath", "#{assets_path}/ace"
ace.config.loadModule "ace/ext/searchbox"
@editor = ace.edit("editor")
@editor.focus()
@editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode
# Before a form submission, move the content from the Ace editor into the
# submitted textarea
$('form').submit =>
$("#file-content").val(@editor.getValue())
@initModePanesAndLinks()
new BlobLicenseSelectors { @editor }
new BlobGitignoreSelectors { @editor }
new BlobCiYamlSelectors { @editor }
initModePanesAndLinks: ->
@$editModePanes = $(".js-edit-mode-pane")
@$editModeLinks = $(".js-edit-mode a")
@$editModeLinks.click @editModeLinkClickHandler
editModeLinkClickHandler: (event) =>
event.preventDefault()
currentLink = $(event.target)
paneId = currentLink.attr("href")
currentPane = @$editModePanes.filter(paneId)
@$editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
@$editModePanes.hide()
currentPane.fadeIn 200
if paneId is "#preview"
$.post currentLink.data("preview-url"),
content: @editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
else
@editor.focus()
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.TemplateSelector = (function() {
function TemplateSelector(opts) {
var ref;
if (opts == null) {
opts = {};
}
this.onClick = bind(this.onClick, this);
this.dropdown = opts.dropdown, this.data = opts.data, this.pattern = opts.pattern, this.wrapper = opts.wrapper, this.editor = opts.editor, this.fileEndpoint = opts.fileEndpoint, this.$input = (ref = opts.$input) != null ? ref : $('#file_name');
this.buildDropdown();
this.bindEvents();
this.onFilenameUpdate();
}
TemplateSelector.prototype.buildDropdown = function() {
return this.dropdown.glDropdown({
data: this.data,
filterable: true,
selectable: true,
toggleLabel: this.toggleLabel,
search: {
fields: ['name']
},
clicked: this.onClick,
text: function(item) {
return item.name;
}
});
};
TemplateSelector.prototype.bindEvents = function() {
return this.$input.on('keyup blur', (function(_this) {
return function(e) {
return _this.onFilenameUpdate();
};
})(this));
};
TemplateSelector.prototype.toggleLabel = function(item) {
return item.name;
};
TemplateSelector.prototype.onFilenameUpdate = function() {
var filenameMatches;
if (!this.$input.length) {
return;
}
filenameMatches = this.pattern.test(this.$input.val().trim());
if (!filenameMatches) {
this.wrapper.addClass('hidden');
return;
}
return this.wrapper.removeClass('hidden');
};
TemplateSelector.prototype.onClick = function(item, el, e) {
e.preventDefault();
return this.requestFile(item);
};
TemplateSelector.prototype.requestFile = function(item) {};
TemplateSelector.prototype.requestFileSuccess = function(file) {
this.editor.setValue(file.content, 1);
return this.editor.focus();
};
return TemplateSelector;
})();
}).call(this);
class @TemplateSelector
constructor: (opts = {}) ->
{
@dropdown,
@data,
@pattern,
@wrapper,
@editor,
@fileEndpoint,
@$input = $('#file_name')
} = opts
@buildDropdown()
@bindEvents()
@onFilenameUpdate()
buildDropdown: ->
@dropdown.glDropdown(
data: @data,
filterable: true,
selectable: true,
toggleLabel: @toggleLabel,
search:
fields: ['name']
clicked: @onClick
text: (item) ->
item.name
)
bindEvents: ->
@$input.on('keyup blur', (e) =>
@onFilenameUpdate()
)
toggleLabel: (item) ->
item.name
onFilenameUpdate: ->
return unless @$input.length
filenameMatches = @pattern.test(@$input.val().trim())
if not filenameMatches
@wrapper.addClass('hidden')
return
@wrapper.removeClass('hidden')
onClick: (item, el, e) =>
e.preventDefault()
@requestFile(item)
requestFile: (item) ->
# To be implemented on the extending class
# e.g.
# Api.gitignoreText item.name, @requestFileSuccess.bind(@)
requestFileSuccess: (file) ->
@editor.setValue(file.content, 1)
@editor.focus()
class @Breakpoints
instance = null;
class BreakpointInstance
BREAKPOINTS = ["xs", "sm", "md", "lg"]
constructor: ->
@setup()
setup: ->
allDeviceSelector = BREAKPOINTS.map (breakpoint) ->
".device-#{breakpoint}"
return if $(allDeviceSelector.join(",")).length
# Create all the elements
els = $.map BREAKPOINTS, (breakpoint) ->
"<div class='device-#{breakpoint} visible-#{breakpoint}'></div>"
$("body").append els.join('')
visibleDevice: ->
allDeviceSelector = BREAKPOINTS.map (breakpoint) ->
".device-#{breakpoint}"
$(allDeviceSelector.join(",")).filter(":visible")
getBreakpointSize: ->
$visibleDevice = @visibleDevice
# the page refreshed via turbolinks
if not $visibleDevice().length
@setup()
$visibleDevice = @visibleDevice()
return $visibleDevice.attr("class").split("visible-")[1]
@get: ->
return instance ?= new BreakpointInstance
$ =>
@bp = Breakpoints.get()
(function() {
this.Breakpoints = (function() {
var BreakpointInstance, instance;
function Breakpoints() {}
instance = null;
BreakpointInstance = (function() {
var BREAKPOINTS;
BREAKPOINTS = ["xs", "sm", "md", "lg"];
function BreakpointInstance() {
this.setup();
}
BreakpointInstance.prototype.setup = function() {
var allDeviceSelector, els;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
if ($(allDeviceSelector.join(",")).length) {
return;
}
els = $.map(BREAKPOINTS, function(breakpoint) {
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
});
return $("body").append(els.join(''));
};
BreakpointInstance.prototype.visibleDevice = function() {
var allDeviceSelector;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
return $(allDeviceSelector.join(",")).filter(":visible");
};
BreakpointInstance.prototype.getBreakpointSize = function() {
var $visibleDevice;
$visibleDevice = this.visibleDevice;
if (!$visibleDevice().length) {
this.setup();
}
$visibleDevice = this.visibleDevice();
return $visibleDevice.attr("class").split("visible-")[1];
};
return BreakpointInstance;
})();
Breakpoints.get = function() {
return instance != null ? instance : instance = new BreakpointInstance;
};
return Breakpoints;
})();
$((function(_this) {
return function() {
return _this.bp = Breakpoints.get();
};
})(this));
}).call(this);
(function() {
$(function() {
var previewPath;
$('input#broadcast_message_color').on('input', function() {
var previewColor;
previewColor = $(this).val();
return $('div.broadcast-message-preview').css('background-color', previewColor);
});
$('input#broadcast_message_font').on('input', function() {
var previewColor;
previewColor = $(this).val();
return $('div.broadcast-message-preview').css('color', previewColor);
});
previewPath = $('textarea#broadcast_message_message').data('preview-path');
return $('textarea#broadcast_message_message').on('input', function() {
var message;
message = $(this).val();
if (message === '') {
return $('.js-broadcast-message-preview').text("Your message here");
} else {
return $.ajax({
url: previewPath,
type: "POST",
data: {
broadcast_message: {
message: message
}
}
});
}
});
});
}).call(this);
$ ->
$('input#broadcast_message_color').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('background-color', previewColor)
$('input#broadcast_message_font').on 'input', ->
previewColor = $(@).val()
$('div.broadcast-message-preview').css('color', previewColor)
previewPath = $('textarea#broadcast_message_message').data('preview-path')
$('textarea#broadcast_message_message').on 'input', ->
message = $(@).val()
if message == ''
$('.js-broadcast-message-preview').text("Your message here")
else
$.ajax(
url: previewPath
type: "POST"
data: { broadcast_message: { message: message } }
)
class @Build
@interval: null
@state: null
constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(Build.interval)
# Init breakpoint checker
@bp = Breakpoints.get()
@hideSidebar()
$('.js-build-sidebar').niceScroll()
$(document)
.off 'click', '.js-sidebar-build-toggle'
.on 'click', '.js-sidebar-build-toggle', @toggleSidebar
$(window)
.off 'resize.build'
.on 'resize.build', @hideSidebar
@updateArtifactRemoveDate()
if $('#build-trace').length
@getInitialBuildTrace()
@initScrollButtonAffix()
if @build_status is "running" or @build_status is "pending"
#
# Bind autoscroll button to follow build output
#
$('#autoscroll-button').on 'click', ->
state = $(this).data("state")
if "enabled" is state
$(this).data "state", "disabled"
$(this).text "enable autoscroll"
else
$(this).data "state", "enabled"
$(this).text "disable autoscroll"
#
# Check for new build output if user still watching build page
# Only valid for runnig build when output changes during time
#
Build.interval = setInterval =>
if window.location.href.split("#").first() is @page_url
@getBuildTrace()
, 4000
getInitialBuildTrace: ->
$.ajax
url: @build_url
dataType: 'json'
success: (build_data) ->
$('.js-build-output').html build_data.trace_html
if build_data.status is 'success' or build_data.status is 'failed'
$('.js-build-refresh').remove()
getBuildTrace: ->
$.ajax
url: "#{@page_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
@state = log.state
if log.status is "running"
if log.append
$('.js-build-output').append log.html
else
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @page_url
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
initScrollButtonAffix: ->
$buildScroll = $('#js-build-scroll')
$body = $('body')
$buildTrace = $('#build-trace')
$buildScroll.affix(
offset:
bottom: ->
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
)
shouldHideSidebar: ->
bootstrapBreakpoint = @bp.getBreakpointSize()
bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
toggleSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
hideSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.removeClass 'right-sidebar-expanded'
.addClass 'right-sidebar-collapsed'
else
$('.js-build-sidebar')
.removeClass 'right-sidebar-collapsed'
.addClass 'right-sidebar-expanded'
updateArtifactRemoveDate: ->
$date = $('.js-artifacts-remove')
if $date.length
date = $date.text()
$date.text $.timefor(new Date(date), ' ')
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.Build = (function() {
Build.interval = null;
Build.state = null;
function Build(page_url, build_url, build_status, state1) {
this.page_url = page_url;
this.build_url = build_url;
this.build_status = build_status;
this.state = state1;
this.hideSidebar = bind(this.hideSidebar, this);
this.toggleSidebar = bind(this.toggleSidebar, this);
clearInterval(Build.interval);
this.bp = Breakpoints.get();
this.hideSidebar();
$('.js-build-sidebar').niceScroll();
$(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
$(window).off('resize.build').on('resize.build', this.hideSidebar);
this.updateArtifactRemoveDate();
if ($('#build-trace').length) {
this.getInitialBuildTrace();
this.initScrollButtonAffix();
}
if (this.build_status === "running" || this.build_status === "pending") {
$('#autoscroll-button').on('click', function() {
var state;
state = $(this).data("state");
if ("enabled" === state) {
$(this).data("state", "disabled");
return $(this).text("enable autoscroll");
} else {
$(this).data("state", "enabled");
return $(this).text("disable autoscroll");
}
});
Build.interval = setInterval((function(_this) {
return function() {
if (window.location.href.split("#").first() === _this.page_url) {
return _this.getBuildTrace();
}
};
})(this), 4000);
}
}
Build.prototype.getInitialBuildTrace = function() {
return $.ajax({
url: this.build_url,
dataType: 'json',
success: function(build_data) {
$('.js-build-output').html(build_data.trace_html);
if (build_data.status === 'success' || build_data.status === 'failed') {
return $('.js-build-refresh').remove();
}
}
});
};
Build.prototype.getBuildTrace = function() {
return $.ajax({
url: this.page_url + "/trace.json?state=" + (encodeURIComponent(this.state)),
dataType: "json",
success: (function(_this) {
return function(log) {
if (log.state) {
_this.state = log.state;
}
if (log.status === "running") {
if (log.append) {
$('.js-build-output').append(log.html);
} else {
$('.js-build-output').html(log.html);
}
return _this.checkAutoscroll();
} else if (log.status !== _this.build_status) {
return Turbolinks.visit(_this.page_url);
}
};
})(this)
});
};
Build.prototype.checkAutoscroll = function() {
if ("enabled" === $("#autoscroll-button").data("state")) {
return $("html,body").scrollTop($("#build-trace").height());
}
};
Build.prototype.initScrollButtonAffix = function() {
var $body, $buildScroll, $buildTrace;
$buildScroll = $('#js-build-scroll');
$body = $('body');
$buildTrace = $('#build-trace');
return $buildScroll.affix({
offset: {
bottom: function() {
return $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top);
}
}
});
};
Build.prototype.shouldHideSidebar = function() {
var bootstrapBreakpoint;
bootstrapBreakpoint = this.bp.getBreakpointSize();
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
};
Build.prototype.toggleSidebar = function() {
if (this.shouldHideSidebar()) {
return $('.js-build-sidebar').toggleClass('right-sidebar-expanded right-sidebar-collapsed');
}
};
Build.prototype.hideSidebar = function() {
if (this.shouldHideSidebar()) {
return $('.js-build-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
} else {
return $('.js-build-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
}
};
Build.prototype.updateArtifactRemoveDate = function() {
var $date, date;
$date = $('.js-artifacts-remove');
if ($date.length) {
date = $date.text();
return $date.text($.timefor(new Date(date.replace(/-/g, '/')), ' '));
}
};
return Build;
})();
}).call(this);
(function() {
this.BuildArtifacts = (function() {
function BuildArtifacts() {
this.disablePropagation();
this.setupEntryClick();
}
BuildArtifacts.prototype.disablePropagation = function() {
$('.top-block').on('click', '.download', function(e) {
return e.stopPropagation();
});
return $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
return e.stopImmediatePropagation();
});
};
BuildArtifacts.prototype.setupEntryClick = function() {
return $('.tree-holder').on('click', 'tr[data-link]', function(e) {
return window.location = this.dataset.link;
});
};
return BuildArtifacts;
})();
}).call(this);
class @BuildArtifacts
constructor: () ->
@disablePropagation()
@setupEntryClick()
disablePropagation: ->
$('.top-block').on 'click', '.download', (e) ->
e.stopPropagation()
$('.tree-holder').on 'click', 'tr[data-link] a', (e) ->
e.stopImmediatePropagation()
setupEntryClick: ->
$('.tree-holder').on 'click', 'tr[data-link]', (e) ->
window.location = @dataset.link
(function() {
this.Commit = (function() {
function Commit() {
$('.files .diff-file').each(function() {
return new CommitFile(this);
});
}
return Commit;
})();
}).call(this);
class @Commit
constructor: ->
$('.files .diff-file').each ->
new CommitFile(this)
(function() {
this.CommitFile = (function() {
function CommitFile(file) {
if ($('.image', file).length) {
new ImageFile(file);
}
}
return CommitFile;
})();
}).call(this);
class @CommitFile
constructor: (file) ->
if $('.image', file).length
new ImageFile(file)
(function() {
this.ImageFile = (function() {
var prepareFrames;
ImageFile.availWidth = 900;
ImageFile.viewModes = ['two-up', 'swipe'];
function ImageFile(file) {
this.file = file;
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
return function(deletedWidth, deletedHeight) {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
if (width === deletedWidth && height === deletedHeight) {
return _this.initViewModes();
} else {
return _this.initView('two-up');
}
});
};
})(this));
}
ImageFile.prototype.initViewModes = function() {
var viewMode;
viewMode = ImageFile.viewModes[0];
$('.view-modes', this.file).removeClass('hide');
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
return function(event) {
if (!$(event.currentTarget).hasClass('active')) {
return _this.activateViewMode(event.currentTarget.className);
}
};
})(this));
return this.activateViewMode(viewMode);
};
ImageFile.prototype.activateViewMode = function(viewMode) {
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
return function() {
$(".view." + viewMode, _this.file).fadeIn(200);
return _this.initView(viewMode);
};
})(this));
};
ImageFile.prototype.initView = function(viewMode) {
return this.views[viewMode].call(this);
};
prepareFrames = function(view) {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
$('.frame', view).each((function(_this) {
return function(index, frame) {
var height, width;
width = $(frame).width();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return maxHeight = height > maxHeight ? height : maxHeight;
};
})(this)).css({
width: maxWidth,
height: maxHeight
});
return [maxWidth, maxHeight];
};
ImageFile.prototype.views = {
'two-up': function() {
return $('.two-up.view .wrap', this.file).each((function(_this) {
return function(index, wrap) {
$('img', wrap).each(function() {
var currentWidth;
currentWidth = $(this).width();
if (currentWidth > ImageFile.availWidth / 2) {
return $(this).width(ImageFile.availWidth / 2);
}
});
return _this.requestImageInfo($('img', wrap), function(width, height) {
$('.image-info .meta-width', wrap).text(width + "px");
$('.image-info .meta-height', wrap).text(height + "px");
return $('.image-info', wrap).removeClass('hide');
});
};
})(this));
},
'swipe': function() {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var ref;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$('.swipe-frame', view).css({
width: maxWidth + 16,
height: maxHeight + 28
});
$('.swipe-wrap', view).css({
width: maxWidth + 1,
height: maxHeight + 2
});
return $('.swipe-bar', view).css({
left: 0
}).draggable({
axis: 'x',
containment: 'parent',
drag: function(event) {
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
},
stop: function(event) {
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
}
});
};
})(this));
},
'onion-skin': function() {
var dragTrackWidth, maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) {
var ref;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$('.onion-skin-frame', view).css({
width: maxWidth + 16,
height: maxHeight + 28
});
$('.swipe-wrap', view).css({
width: maxWidth + 1,
height: maxHeight + 2
});
return $('.dragger', view).css({
left: dragTrackWidth
}).draggable({
axis: 'x',
containment: 'parent',
drag: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
},
stop: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
}
});
};
})(this));
}
};
ImageFile.prototype.requestImageInfo = function(img, callback) {
var domImg;
domImg = img.get(0);
if (domImg) {
if (domImg.complete) {
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
} else {
return img.on('load', (function(_this) {
return function() {
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
};
})(this));
}
}
};
return ImageFile;
})();
}).call(this);
class @ImageFile
# Width where images must fits in, for 2-up this gets divided by 2
@availWidth = 900
@viewModes = ['two-up', 'swipe']
constructor: (@file) ->
# Determine if old and new file has same dimensions, if not show 'two-up' view
this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) =>
this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) =>
if width == deletedWidth && height == deletedHeight
this.initViewModes()
else
this.initView('two-up')
initViewModes: ->
viewMode = ImageFile.viewModes[0]
$('.view-modes', @file).removeClass 'hide'
$('.view-modes-menu', @file).on 'click', 'li', (event) =>
unless $(event.currentTarget).hasClass('active')
this.activateViewMode(event.currentTarget.className)
this.activateViewMode(viewMode)
activateViewMode: (viewMode) ->
$('.view-modes-menu li', @file)
.removeClass('active')
.filter(".#{viewMode}").addClass 'active'
$(".view:visible:not(.#{viewMode})", @file).fadeOut 200, =>
$(".view.#{viewMode}", @file).fadeIn(200)
this.initView viewMode
initView: (viewMode) ->
this.views[viewMode].call(this)
prepareFrames = (view) ->
maxWidth = 0
maxHeight = 0
$('.frame', view).each (index, frame) =>
width = $(frame).width()
height = $(frame).height()
maxWidth = if width > maxWidth then width else maxWidth
maxHeight = if height > maxHeight then height else maxHeight
.css
width: maxWidth
height: maxHeight
[maxWidth, maxHeight]
views:
'two-up': ->
$('.two-up.view .wrap', @file).each (index, wrap) =>
$('img', wrap).each ->
currentWidth = $(this).width()
if currentWidth > ImageFile.availWidth / 2
$(this).width ImageFile.availWidth / 2
this.requestImageInfo $('img', wrap), (width, height) ->
$('.image-info .meta-width', wrap).text "#{width}px"
$('.image-info .meta-height', wrap).text "#{height}px"
$('.image-info', wrap).removeClass('hide')
'swipe': ->
maxWidth = 0
maxHeight = 0
$('.swipe.view', @file).each (index, view) =>
[maxWidth, maxHeight] = prepareFrames(view)
$('.swipe-frame', view).css
width: maxWidth + 16
height: maxHeight + 28
$('.swipe-wrap', view).css
width: maxWidth + 1
height: maxHeight + 2
$('.swipe-bar', view).css
left: 0
.draggable
axis: 'x'
containment: 'parent'
drag: (event) ->
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
stop: (event) ->
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
'onion-skin': ->
maxWidth = 0
maxHeight = 0
dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width()
$('.onion-skin.view', @file).each (index, view) =>
[maxWidth, maxHeight] = prepareFrames(view)
$('.onion-skin-frame', view).css
width: maxWidth + 16
height: maxHeight + 28
$('.swipe-wrap', view).css
width: maxWidth + 1
height: maxHeight + 2
$('.dragger', view).css
left: dragTrackWidth
.draggable
axis: 'x'
containment: 'parent'
drag: (event) ->
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
stop: (event) ->
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
requestImageInfo: (img, callback) ->
domImg = img.get(0)
if domImg
if domImg.complete
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
else
img.on 'load', =>
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
(function() {
this.CommitsList = (function() {
function CommitsList() {}
CommitsList.timer = null;
CommitsList.init = function(limit) {
$("body").on("click", ".day-commits-table li.commit", function(event) {
if (event.target.nodeName !== "A") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
Pager.init(limit, false);
this.content = $("#commits-list");
this.searchField = $("#commits-search");
return this.initSearch();
};
CommitsList.initSearch = function() {
this.timer = null;
return this.searchField.keyup((function(_this) {
return function() {
clearTimeout(_this.timer);
return _this.timer = setTimeout(_this.filterResults, 500);
};
})(this));
};
CommitsList.filterResults = function() {
var commitsUrl, form, search;
form = $(".commits-search-form");
search = CommitsList.searchField.val();
commitsUrl = form.attr("action") + '?' + form.serialize();
CommitsList.content.fadeTo('fast', 0.5);
return $.ajax({
type: "GET",
url: form.attr("action"),
data: form.serialize(),
complete: function() {
return CommitsList.content.fadeTo('fast', 1.0);
},
success: function(data) {
CommitsList.content.html(data.html);
return history.replaceState({
page: commitsUrl
}, document.title, commitsUrl);
},
dataType: "json"
});
};
return CommitsList;
})();
}).call(this);
class @CommitsList
@timer = null
@init: (limit) ->
$("body").on "click", ".day-commits-table li.commit", (event) ->
if event.target.nodeName != "A"
location.href = $(this).attr("url")
e.stopPropagation()
return false
Pager.init limit, false
@content = $("#commits-list")
@searchField = $("#commits-search")
@initSearch()
@initSearch: ->
@timer = null
@searchField.keyup =>
clearTimeout(@timer)
@timer = setTimeout(@filterResults, 500)
@filterResults: =>
form = $(".commits-search-form")
search = @searchField.val()
commitsUrl = form.attr("action") + '?' + form.serialize()
@content.fadeTo('fast', 0.5)
$.ajax
type: "GET"
url: form.attr("action")
data: form.serialize()
complete: =>
@content.fadeTo('fast', 1.0)
success: (data) =>
@content.html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: commitsUrl}, document.title, commitsUrl
dataType: "json"
(function() {
this.Compare = (function() {
function Compare(opts) {
this.opts = opts;
this.source_loading = $(".js-source-loading");
this.target_loading = $(".js-target-loading");
$('.js-compare-dropdown').each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return $dropdown.glDropdown({
selectable: true,
fieldName: $dropdown.data('field-name'),
filterable: true,
id: function(obj, $el) {
return $el.data('id');
},
toggleLabel: function(obj, $el) {
return $el.text().trim();
},
clicked: function(e, el) {
if ($dropdown.is('.js-target-branch')) {
return _this.getTargetHtml();
} else if ($dropdown.is('.js-source-branch')) {
return _this.getSourceHtml();
} else if ($dropdown.is('.js-target-project')) {
return _this.getTargetProject();
}
}
});
};
})(this));
this.initialState();
}
Compare.prototype.initialState = function() {
this.getSourceHtml();
return this.getTargetHtml();
};
Compare.prototype.getTargetProject = function() {
return $.ajax({
url: this.opts.targetProjectUrl,
data: {
target_project_id: $("input[name='merge_request[target_project_id]']").val()
},
beforeSend: function() {
return $('.mr_target_commit').empty();
},
success: function(html) {
return $('.js-target-branch-dropdown .dropdown-content').html(html);
}
});
};
Compare.prototype.getSourceHtml = function() {
return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', {
ref: $("input[name='merge_request[source_branch]']").val()
});
};
Compare.prototype.getTargetHtml = function() {
return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', {
target_project_id: $("input[name='merge_request[target_project_id]']").val(),
ref: $("input[name='merge_request[target_branch]']").val()
});
};
Compare.prototype.sendAjax = function(url, loading, target, data) {
var $target;
$target = $(target);
return $.ajax({
url: url,
data: data,
beforeSend: function() {
loading.show();
return $target.empty();
},
success: function(html) {
loading.hide();
$target.html(html);
return $('.js-timeago', $target).timeago();
}
});
};
return Compare;
})();
}).call(this);
class @Compare
constructor: (@opts) ->
@source_loading = $ ".js-source-loading"
@target_loading = $ ".js-target-loading"
$('.js-compare-dropdown').each (i, dropdown) =>
$dropdown = $(dropdown)
$dropdown.glDropdown(
selectable: true
fieldName: $dropdown.data 'field-name'
filterable: true
id: (obj, $el) ->
$el.data 'id'
toggleLabel: (obj, $el) ->
$el.text().trim()
clicked: (e, el) =>
if $dropdown.is '.js-target-branch'
@getTargetHtml()
else if $dropdown.is '.js-source-branch'
@getSourceHtml()
else if $dropdown.is '.js-target-project'
@getTargetProject()
)
@initialState()
initialState: ->
@getSourceHtml()
@getTargetHtml()
getTargetProject: ->
$.ajax(
url: @opts.targetProjectUrl
data:
target_project_id: $("input[name='merge_request[target_project_id]']").val()
beforeSend: ->
$('.mr_target_commit').empty()
success: (html) ->
$('.js-target-branch-dropdown .dropdown-content').html html
)
getSourceHtml: ->
@sendAjax(@opts.sourceBranchUrl, @source_loading, '.mr_source_commit',
ref: $("input[name='merge_request[source_branch]']").val()
)
getTargetHtml: ->
@sendAjax(@opts.targetBranchUrl, @target_loading, '.mr_target_commit',
target_project_id: $("input[name='merge_request[target_project_id]']").val()
ref: $("input[name='merge_request[target_branch]']").val()
)
sendAjax: (url, loading, target, data) ->
$target = $(target)
$.ajax(
url: url
data: data
beforeSend: ->
loading.show()
$target.empty()
success: (html) ->
loading.hide()
$target.html html
$('.js-timeago', $target).timeago()
)
(function() {
this.CompareAutocomplete = (function() {
function CompareAutocomplete() {
this.initDropdown();
}
CompareAutocomplete.prototype.initDropdown = function() {
return $('.js-compare-dropdown').each(function() {
var $dropdown, selected;
$dropdown = $(this);
selected = $dropdown.data('selected');
return $dropdown.glDropdown({
data: function(term, callback) {
return $.ajax({
url: $dropdown.data('refs-url'),
data: {
ref: $dropdown.data('ref')
}
}).done(function(refs) {
return callback(refs);
});
},
selectable: true,
filterable: true,
filterByText: true,
fieldName: $dropdown.attr('name'),
filterInput: 'input[type="text"]',
renderRow: function(ref) {
var link;
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
} else {
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
return $('<li />').append(link);
}
},
id: function(obj, $el) {
return $el.attr('data-ref');
},
toggleLabel: function(obj, $el) {
return $el.text().trim();
}
});
});
};
return CompareAutocomplete;
})();
}).call(this);
class @CompareAutocomplete
constructor: ->
@initDropdown()
initDropdown: ->
$('.js-compare-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: $dropdown.attr('name')
filterInput: 'input[type="text"]'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
)
(function() {
this.ConfirmDangerModal = (function() {
function ConfirmDangerModal(form, text) {
var project_path, submit;
this.form = form;
$('.js-confirm-text').text(text || '');
$('.js-confirm-danger-input').val('');
$('#modal-confirm-danger').modal('show');
project_path = $('.js-confirm-danger-match').text();
submit = $('.js-confirm-danger-submit');
submit.disable();
$('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() {
if (rstrip($(this).val()) === project_path) {
return submit.enable();
} else {
return submit.disable();
}
});
$('.js-confirm-danger-submit').off('click');
$('.js-confirm-danger-submit').on('click', (function(_this) {
return function() {
return _this.form.submit();
};
})(this));
}
return ConfirmDangerModal;
})();
}).call(this);
class @ConfirmDangerModal
constructor: (form, text) ->
@form = form
$('.js-confirm-text').text(text || '')
$('.js-confirm-danger-input').val('')
$('#modal-confirm-danger').modal('show')
project_path = $('.js-confirm-danger-match').text()
submit = $('.js-confirm-danger-submit')
submit.disable()
$('.js-confirm-danger-input').off 'input'
$('.js-confirm-danger-input').on 'input', ->
if rstrip($(@).val()) is project_path
submit.enable()
else
submit.disable()
$('.js-confirm-danger-submit').off 'click'
$('.js-confirm-danger-submit').on 'click', =>
@form.submit()
/*= require clipboard */
(function() {
var genericError, genericSuccess, showTooltip;
genericSuccess = function(e) {
showTooltip(e.trigger, 'Copied!');
e.clearSelection();
return $(e.trigger).blur();
};
genericError = function(e) {
var key;
if (/Mac/i.test(navigator.userAgent)) {
key = '&#8984;';
} else {
key = 'Ctrl';
}
return showTooltip(e.trigger, "Press " + key + "-C to copy");
};
showTooltip = function(target, title) {
return $(target).tooltip({
container: 'body',
html: 'true',
placement: 'auto bottom',
title: title,
trigger: 'manual'
}).tooltip('show').one('mouseleave', function() {
return $(this).tooltip('hide');
});
};
$(function() {
var clipboard;
clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
clipboard.on('success', genericSuccess);
return clipboard.on('error', genericError);
});
}).call(this);
#= require clipboard
genericSuccess = (e) ->
showTooltip(e.trigger, 'Copied!')
# Clear the selection and blur the trigger so it loses its border
e.clearSelection()
$(e.trigger).blur()
# Safari doesn't support `execCommand`, so instead we inform the user to
# copy manually.
#
# See http://clipboardjs.com/#browser-support
genericError = (e) ->
if /Mac/i.test(navigator.userAgent)
key = '&#8984;' # Command
else
key = 'Ctrl'
showTooltip(e.trigger, "Press #{key}-C to copy")
showTooltip = (target, title) ->
$(target).
tooltip(
container: 'body'
html: 'true'
placement: 'auto bottom'
title: title
trigger: 'manual'
).
tooltip('show').
one('mouseleave', -> $(this).tooltip('hide'))
$ ->
clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]'
clipboard.on 'success', genericSuccess
clipboard.on 'error', genericError
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment