Compare commits

...

97 Commits

Author SHA1 Message Date
restitux 211445907c Merge branch 'autoredirect-v5.3.0-dev' into wiki.ohea.xyz-dev 2023-01-17 18:57:31 -07:00
restitux e750090244 Add new wip autoredirect code 2023-01-17 18:50:52 -07:00
restitux 5eeb73cb0a Omniauth / omnigollum integration
Based on: https://github.com/remyd1/gollum/commit/925ebcfe04f294c979b9e088dc0955ca1dfa8c44
2023-01-17 02:18:32 -07:00
Dawa Ometto 9c2f8dfeba Release 5.3.0 2022-05-25 11:24:19 +02:00
Dawa Ometto 8e35688b17 Release preparation: update readme, rakefile and changelog (#1788)
* Update README.md
* Add LATEST_CHANGES.md and release helpers
2022-05-24 16:27:39 +02:00
Paul Westcott 860e8b2ebd Restore the normalize check that appears to be inadvertantly removed (#1802)
* Add test for normalize on upload

Co-authored-by: Dawa Ometto <d.ometto@gmail.com>
2022-05-24 16:13:35 +02:00
benjamin wil 0d2ab11604 Include rdoc as a dependency (#1823)
We've had some reports (#1342, #1809) of `rdoc` not being installed
in some environments, causing Gollum to error in unpleasant ways.

This isn't an issue that's unique to Gollum, and I don't think `rdoc`
should actually be a hard dependency of this project.  But since we can
side-step the issue by requiring `rdoc`, something that most users who
install Gollum via RubyGems would already have installed, I don't think
this is a horrible solution.
2022-05-04 18:25:48 -07:00
Andreas Wachter ddc7dba0a2 Remove arm/v6 and v7 from the Docker builds (#1822)
* added platforms to actions, install libc6-compat for pi

* fix build problem for now. https://github.com/gollum/gollum/runs/6248371141
2022-05-03 08:15:09 -07:00
Andreas Wachter bc3503f374 added platforms to actions, install libc6-compat for pi (#1806) 2022-04-30 23:04:45 -07:00
benjamin wil 2dfe103687 Ensure example git repos are valid (#1821)
* Ensure example git repos are valid

It was reported in #1817 that the `lotr_migration.git` repository we use
in `test/test_migrate.rb` is not a valid git repository on when clone,
causing the test suite to fail when run locally.

The reason is because there is no `.git/refs` directory, meaning it's
not really a valid git repository at all. I noticed that the `empty.git`
example repository has the same problem.

This commit simply ensures that the directory structure of these example
repositories are persisted in git.

* Run `test_migrate` tests on CI

Now that we've resolved the issue with the invalid git repository in the
parent commit, we can run all of the tests in our CI environment.
2022-04-30 23:03:56 -07:00
benjamin wil 95d35d38da Switch from TestUnit to Minitest (#1805)
* Use `Minitest::Test`

`Test::Unit` is deprecated, and we can switch to `Minitest::Test` with
almost no side effects.

This commit does all of the work required to make Minitest tests run
with Gollum's existing test helpers.

* Change Minitest output format

- The `DefaultReporter` seems to have cleaner output than what we had
  before.
- `color: true` ensures things are colorized. It's pretty nice.

* Tweak test formatting; fix order-dependent failure

The order-dependent failure has been been commented in code.

After manually bisecting the test suite, I was able to determine that
the template cascade tests were leaving the `@@template_priority_path`
set to an overridden value in tests run afterward.

* Tweak setting initialization

I could not see a meaningful reason behind calling `forbid` so early in
the settings initialization block. But moving the `forbid` call until
later resolved this issue.

In the real world, I don't see how this would cause issues. But I found
that calling `forbid` when `wiki_options[:allow_editing]` was set to
false caused order-dependent test errors where Sprockets would end up
being badly configured and left without an initialized
`Sprockets::Environment` object, which is required by the
`sprockets-helpers` gem to resolve asset paths.

The test that would cause errors is also in this commit diff. I've
updated it to be a bit more readable.

I also took this opportunity to review and clean up the `@allow_editing`
assignment, which seemed a bit obfuscated.

* Update migration script to test run warnings

When running the entire test suite, many warnings would be output:

/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: already initialized constant HYPHENATE
/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: previous definition of HYPHENATE was here
/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: already initialized constant PAGE_FILE_DIR
/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: previous definition of PAGE_FILE_DIR was here
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:91: warning: already initialized constant REPO
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:91: warning: previous definition of REPO was here
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:236: warning: already initialized constant TREE
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:236: warning: previous definition of TREE was here
/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: already initialized constant PAGE_FILE_DIR
/Users/bw/Projects/gollum/test/test_migrate.rb:37: warning: previous definition of PAGE_FILE_DIR was here
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:91: warning: already initialized constant REPO
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:91: warning: previous definition of REPO was here
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:236: warning: already initialized constant TREE
/Users/bw/Projects/gollum/bin/gollum-migrate-tags:236: warning: previous definition of TREE was here

While it's unlikely that end users would ever see these warnings, as
they'd only be running the migration script once, they will always be
shown in our local test runs and CI run output.

So instead of using constants in our migration script, I change the
script to use class variables instead. This should not effect the
functionality of the migration script whatsoever.

* Use `File.exist?` instead of `File.exists?`

In Ruby 3.x, `File.exists?` is deprecated and outputs a warning.

* Improve "allow editing" tests

While making changes to the test suite, I ran into some issues with
these tests failing on occasion.

I added some setup and teardown to help with this.

But one thing I did notice is that the word "Upload" appears in the
response body whether uploading is enabled or not, so I made these
assertions more specific to the HTML rather than other places the word
"Upload" might appear (which is related to Critic Markup).

* Do not attempt to modify frozen strings

Using `#gsub!` here now results in an error. I am not entirely sure why
this hasn't happened before, but the error is straightforward:

     FrozenError: can't modify frozen String: "Author %{author} is from %{location}"
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:45:in `gsub!'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:45:in `fill_argument_content'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:37:in `block in autofill'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `each'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `map'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `autofill'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:35:in `block in autofill'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `each'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `map'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:33:in `autofill'
        /home/runner/work/gollum/gollum/lib/gollum/views/helpers/locale_helpers.rb:21:in `t'
        /home/runner/work/gollum/gollum/test/gollum/views/test_locale_helper.rb:95:in `block (4 levels) in <top (required)>'

`#gsub!` attempts to modify a string in-place, which does not work when
a string is frozen. It seems that in some Ruby environments, strings
from YAML files are frozen.

* Fix another order-dependent test failure

Very occasionally, this test fails due to `Precious::App` settings set
in previous tests. You can reproduce this failure using this seed:

    bundle exec rake TESTOPTS="--seed=42898"
2022-04-27 08:25:54 -07:00
Tevin 81c90e55a7 Add page context to template filter, resolves #1603 (#1818)
* Add a test for template filter
* Add a test for template filter with page parameter
* Passing page as a parameter to template filter
2022-04-25 09:44:19 +02:00
mishina 3f0b61081b Add Ruby 3.1 to the CI matrix (#1812)
* Enclose all the version numbers in quotes
2022-03-23 15:30:49 +01:00
mishina ecc317886a Remove fields from gemspec that should not be set (#1811) 2022-03-23 13:27:54 +01:00
Dawa Ometto 4b2dc8e5c0 Set encoding explicitly in bin/gollum (#1801)
Resolves #1699
2022-01-13 21:45:49 +01:00
Nikita Ivanov 6f870501a0 Add option to show browser's local time (#1653)
* Add tests for --local-time option
* Update Readme
2022-01-13 18:19:22 +01:00
Nikita Ivanov f30058f4ee Fix broken History button when viewing historical deleted file (#1759)
* Fix broken History button when viewing historical deleted file
* Add tests
2022-01-13 18:06:13 +01:00
Dawa Ometto 82ce013cab Fix Docker Deploy workflow (#1800) 2022-01-12 11:34:42 +01:00
Dawa Ometto 0c36cdf5ba Support versioned Docker builds (#1799) 2022-01-12 11:04:12 +01:00
Dawa Ometto 6159fcd4a2 Remove explicit nokogori version requirement. Resolves #1779 (#1798)
* Remove explicit nokogori version requirement
* Require ruby >= 2.6, use latest JRuby

No longer needed after nokogiri's recent release, see https://github.com/gollum/gollum/issues/1779
2022-01-11 17:51:14 +01:00
benjamin wil 8f7793f461 Improve presentation of page navigation and #head area on mobile devices (#1742)
* Add `.header-title` style

This style shows smaller text on mobile devices. It reverts back to the
`2em` size used in Primer for H1 tags.

* Use `.header-title` for all `#head` headings

This commit:

  1. Updates all main templates to use the new `.header-title` class,
     making these titles less disruptively large on mobile devices.
  2. Centers these titles for mobile breakpoints only, re-setting them
     back to left-aligned text for `md-` breakpoints and up.

Note that in `lib/gollum/templates/wiki_contentf.mustache`, the
`.header-title` is not a `#head .header-title`, so it doesn't inherit
all of the new styles added in this commit.

* Adjust padding and presentation of mobile menu

To give mobile page headers more structure, this commit adjusts the
padding to be more symmetrical. It also adds a `border-bottom` on mobile
only. In my opinion this improves readability by telling the reader,
"This is where the navigation menu ends and the page content begins."

* Recompile assets

* Fix failing spec

My changes to the header layout seem to changed where the spaces in
`last_response.body` are placed. So let's assert only that the content
is present, since that's all this test should actually care about.
2021-12-30 14:36:51 -08:00
James Reynolds 589b4bce39 Added docker environment variables for author and email (#1789)
Sets git user.name and user.email if env var is set
2021-12-30 14:27:20 -08:00
benjamin wil 9c574fd760 Improve test suite and CI run performance (#1796)
* Simplify test

I came across this test because it was failing JRuby CI runs. I should
emphasize, though, that this test was only failing due do an upstream
bug in Nokogiri v1.12.5.

When I was reviewing the test, to understand why it was failing at all,
I noticed that the assertion was rather obfuscated.  Specifically, we
were post-processing the entire `response.body` and using that as an
expectation.

* Use an older version of Nokogiri in our test env

This is a temporary fix. See the commit diff for more information.

* Split JRuby CI runs from MRI CI runs

We can improve the performance of our MRI test runs by not installing
Java as part of their run. (Java is only required for JRuby.)

* Use latest Ruby patch versions during CI runs

* Simplify GitHub Action workflow steps

Judging by the output in the GitHub Actions workflow UI, the `echo`
steps were not providing much value to us.

We can get rid of them to slightly increase run performance.

I've also named the other steps to make it easier to skim the Actions
workflow output.

* Remove `twitter_cldr` development dependency

This is no longer being used.

* Change GitHub Actions workflow Ruby matrix

We can drop Ruby 2.4 from our test run matrix. It is beyond EOL.
Let's add 2.7 instead.
2021-12-30 14:21:23 -08:00
Dawa Ometto 46c22a8b87 Take account of possible https referer in upload route (#1787) 2021-12-23 12:18:30 +01:00
Ryan Govostes 7e379cfab1 Reduce size of container image (#1777)
* Reduce size of container image
* Add deprecation message for automatic --mathjax
2021-12-22 20:54:29 +01:00
Dawa Ometto 93d3d10453 Update gollum (#1786)
Exit with code 1 (error) when invalid option is given
2021-12-22 18:08:20 +01:00
fhchl 98a0006c86 Fix mathjax on edit and create pages (#1773)
* Fix mathjax on edit and create pages
2021-12-22 17:00:31 +01:00
Dawa Ometto d97721f38b Update test.yaml
Update JDK
2021-11-06 13:32:13 +01:00
Aaron Wallentine 8d3ec8605e Small grammar fix in README.md (#1776) 2021-10-28 13:55:32 +02:00
Brian Porter 7517389072 Allow for overriding only specific Mustache templates/partials. (#1719)
* Allow for overriding only specific templates/partials. Resolves  #1450.
2021-09-07 17:32:46 +02:00
Dawa Ometto b7011139cf Create release.yml (#1760) 2021-09-06 11:30:56 +02:00
Dawa Ometto 6e8a68dd0d Delete .travis.yml 2021-09-02 21:04:30 +02:00
Dawa Ometto 4e8309d3e4 Load template page as utf-8 (#1758) 2021-08-30 11:09:29 +02:00
Dawa Ometto 371ab21d16 Add SauceLab attribution 2021-08-28 17:36:06 +02:00
yy0931 cbcbc4bc3f unescape page names in the preview tab (#1739)
* Unescape page names in the preview tab

* Execute rake precompile
2021-08-28 17:24:00 +02:00
Dawa Ometto dc9b2e1766 Update gollum.gemspec (#1749) 2021-08-28 17:21:44 +02:00
Darless 334df62651 Docker support within the repository (#1732)
- Adds a Dockerfile within this repository
- GitHub actions to build and test the image
2021-08-03 12:52:47 +02:00
yy0931 046353cf7e Fix an IME rendering issue (#1735)
* Fix an IME rendering issue

* Execute rake precompile
2021-07-09 07:52:38 -07:00
benjamin wil 51f2f032d7 Add I18n interface for use in Mustache templates (#1679)
* Add `i18n` dependency

We will use `i18n` to provide localization for Gollum's frontend. I
chose this because it's a well-supported, pretty normal Ruby library.

* Configure I18n

- Locale files will be kept in `lib/gollum/locales/[lang].yml`
- The available locales, to start, will be English (`en`).

* Add I18n interface for mustache templates

This commit adds an interface that allows mustache templates to get I18n
translation strings, transform any arguments that may be present in
them, and then render them on the frontend.

This is our first real step to getting internationalizing the Gollum
frontend.
2021-06-27 09:26:45 -07:00
Dawa Ometto 70360edb96 Update test.yaml (#1734) 2021-06-26 22:06:34 +02:00
benjamin wil 2b12ab9206 Explicitly set encoding for Precious::App (#1731)
An issue was reported (see #1721) where, in docker containers where the
`LANG` was not being set, `Precious::App` would serve Mustache templates
in an ASCII encoding. This caused templates to error out.

In the past, this would have happened in environments where `LANG` was
not set and the Gollum applciation configuration had enabled MathJax.
Now, it happens even if MathJax is disabled because of a UTF-8 character
I added to the mobile navigation menu.

Basically, we should enforce a UTF-8 encoding from now on to avoid
runtime errors related to ASCII encoding.
2021-06-14 21:28:12 +02:00
Darless 87a01e04ce fixes #1723: Github Actions Supported (#1729)
* fixes #1723: Github Actions Supported

- This is to switch from Travis CI which is shutting down the org version,
  and the .com version, a free account is limited.
- Switch test environment variable from TRAVIS to CI to be more generic.

* Adding jruby-9.2.18.0 to matrix for github actions
2021-06-14 21:21:40 +02:00
Sam 5f04200bd0 README.md: Clarify that Gollum should run against an already-initialized Git repository. (#1722) 2021-06-13 14:34:28 -07:00
benjamin wil aa823b5a2d Use recent JRuby release for Travis CI (#1730)
Our JRuby CI runs have been errorring due to what I think is an issue
with an older `jruby-openssl` version.
2021-06-13 14:17:18 -07:00
Dawa Ometto 9012dee888 Release 5.2.3 2021-04-18 13:43:00 +02:00
benjamin wil 81d5f1a8bb Ensure <title> is rendered for pages (#1710)
I made a mistake when I made `#title` a private method. I did not see
that it was being called from `layout.mustache` to generate the
`<title></title>` tag in the `<head></head>` of each page.

This fixes my error, and adds tests so the behaviour is more explicit.
2021-04-05 09:26:50 -07:00
Dawa Ometto d2b0a22a8f Release 5.2.2 2021-03-27 19:43:38 +01:00
Dawa Ometto 355e6b1f18 Fix query ? in mathjax script path (#1706) 2021-03-27 19:40:35 +01:00
Nikita Ivanov 7a2c9107c3 Delete dublicate error (#1700) 2021-03-27 14:05:03 +01:00
benjamin wil 127473fff8 Fix tab navigation styles (#1696)
* Fix tabnav styles on #create and #edit views

The Primer CSS-provided `tabnav` styles were not being used on the Edit
and Preview tabs on the create and edit pages.

After following Primer's documentation [1], it looks like we were using
the `aria-current` attribute incorrectly. Dynamically adding/removing
this attribute on the selected tab fixes the issue.

[1]: https://primer-css-git-next-inputs.primer.now.sh/css/components/navigation#tabnav

* Recompile static assets
2021-03-23 09:13:23 -07:00
benjamin wil 9a79b0a800 Merge pull request #1701 from ViChyavIn/fix-focused-button
Fix focused button border shown wrong in dialogs
2021-03-23 07:49:52 -07:00
ViChyavIn ca13298d00 Update static assets 2021-03-23 13:43:26 +05:00
ViChyavIn eaf82e6367 Overflow is set to visible by default so declaration is removed 2021-03-23 13:39:44 +05:00
benjamin wil cae290ded7 Merge pull request #1697 from gollum/benjaminwil/fix-nav-outline
Fix nav outline styles
2021-03-22 10:21:29 -07:00
Benjamin Willems a22208a0be Use <button> instead of <a> without href
It is more semantic to use a `<button>` tag in the place of an `<a>`
when there is no other page being linked to. In this circumstance, we're
using JavaScript to present a modal to the user on click.

This change makes the "Upload" and "Rename" buttons appear in the
browser's tab index.
2021-03-21 14:21:07 -07:00
Benjamin Willems bc877dc9dc Fix indentation due to DOM simplification
We removed one of the parent elements of the `<nav>`, so we must
re-indent the entire file.
2021-03-21 14:19:32 -07:00
Benjamin Willems 40b1775d42 Make the main <nav> element a TableObject
There was a display issue, where navbar items's outline styles were
being cut off due to the parent `<nav>` element's margin and padding.

Fortunately, we can do away with the navbar wrapper div entirely. It was
not doing anything important except defining its children as
`TableObject` items. But we can just do this on the `<nav>` itself.
2021-03-21 14:18:45 -07:00
ViChyavIn c2dc605adb Fix focused button border shown wrong 2021-03-20 21:43:53 +05:00
benjamin wil a1e1af07a4 Merge pull request #1676 from gollum/benjaminwil/button-labels
Move "Page History" into button group
2021-03-15 08:24:04 -07:00
benjamin wil 76948130f6 Display button groups using Primer flex utilities
This small improvement just uses Primer `d-flex` utilities to `display:
flex;` instead of using style tags. It's preferable.
2021-03-06 16:19:01 -08:00
Benjamin Willems f71ba31bfe Move "Page History" into button group
If the user has edit permissions, the "History" button is shown in a
button group with "Edit" and "Rename". If the user does not have edit
permissions, it's shown by itself.

This commit also renames the label from "Page History" to "History".
2021-03-06 16:19:01 -08:00
Dawa Ometto b7caa228e6 Add webrick dependency (#1695)
* Remove webrick as dev dependency
2021-03-06 15:39:21 +01:00
Dawa Ometto 0101dd2f65 Update .travis.yml (#1690) 2021-03-06 10:06:11 +01:00
Dawa Ometto c8baa61fe3 Update image icon in editor (#1687) 2021-02-26 15:30:02 +01:00
Dawa Ometto 104335706a Faster tests (#1686) 2021-02-26 11:34:41 +01:00
Dawa Ometto 7c60841bad Release 5.2.1 2021-02-25 13:35:02 +01:00
benjamin wil f6a7c57175 Don't include primer.css separate from app.css (#1685)
* Recompile static assets
2021-02-25 09:39:41 +01:00
Dawa Ometto 730acee609 Release 5.2 2021-02-24 12:06:54 +01:00
Dawa Ometto b7295df662 Fix Compare view for renames on JRuby (#1682) 2021-02-24 11:53:48 +01:00
Dawa Ometto aba3ec37d9 Fix styling for Latest Changes flexbox (#1683) 2021-02-24 09:28:34 +01:00
Dawa Ometto 020c98bfad Update README.md (#1670) 2021-02-23 12:47:30 +01:00
Dawa Ometto d33d11e086 Make Keybinding and Markup selects smaller (#1681) 2021-02-23 12:47:03 +01:00
benjamin wil 2e41751cf6 Fix sidebar and footer padding (#1677)
* Change footer and footer container spacing

This commit:

  1. Removes the Primer `my-md-0` spacing from the `#wiki-footer`
     container.

     This gives it margins along the Y axis, spacing it out from
     the sidebar and main wiki content containers on `md` and larger
     screens.

  2. Adds `px-4` padding to the `#footer-content`. which makes the

     This makes the footer content inset the same way that sidebar
     content is. This makes the content look more uniform on mobile
     devices, when the sidebar and footer are presented one before
     the other.

* Change spacing behaviour of `#wiki-sidebar`

To make `#wiki-sidebar` and `#wiki-footer` more similar, we can make
sure `px-4` padding is applied to the `-content` container, rather than
the outer container.
2021-02-22 09:32:02 +01:00
benjamin wil f493150825 Fix heading counters and anchor icon presentation. Resolves #1642 (#1673)
* Reset h2 counters
* Reformat .header-enum scoped SCSS
* Show overflow .wiki-body content
* Re-scope and re-style heading `.anchor` elements
* Remove unused SCSS
* Recompile static assets

Co-authored-by: Dawa Ometto <dawa.ometto@uni-leipzig.de>
2021-02-20 11:10:52 +01:00
benjamin wil 68d0dd0cd5 Add mobile navigation menu (#1671)
* If navbar is too wide, use overflow-x: scroll
* Add initial mobile nav menu
* Use CSS classes instead of IDs for jQuery events
* Change content x-axis padding for mobile devices
* Recompile assets for mobile menu feature
2021-02-18 09:33:33 +01:00
benjamin wil 38fc7e69c1 Update Primer CSS to v15.2.0 (#1659) 2021-02-14 01:05:10 +01:00
benjamin wil 5b7b9f40ae Only escape HTML from breadcrumbs. Resolves #1658 (#1663) 2021-02-14 01:04:42 +01:00
benjamin wil 17162aa091 Make sidebar layout mobile-friendly (#1660) 2021-02-13 18:29:33 +01:00
benjamin wil 0c89aa2b3e Update reference to test git bare repositories (#1662)
The CONTRIBUTING document referenced a file that has since been moved.
2021-02-13 17:50:27 +01:00
benjamin wil 1980e39876 Remove unused _layout.scss file (#1661) 2021-02-13 17:25:54 +01:00
Garet Robertson 2b949014b8 Add JS polyfill for IE compatibility (#1664). (#1667) 2021-02-13 17:11:40 +01:00
Dawa Ometto b399496ec2 Update gems (#1637) 2021-02-13 16:48:28 +01:00
Sam 8433327926 contrib/automation/gollum-post: A script to post files to Gollum in an automated way. (#1608) 2021-02-13 16:48:11 +01:00
Nikita Ivanov bb207f43d0 Fix binary diffs unable to be viewed in history. Resolves #1650 (#1649)
* Fix binary diffs cause error when viewed
* Change color of git lines to #000000a0
* Update assets
2021-01-10 13:54:30 +01:00
Nikita Ivanov 333af9b76c Add redirect to rename commit (#1648)
This PR ensures that changes to `REDIRECTS_FILE` are committed with the commit responsible for the corresponding git rename.
2021-01-08 14:36:54 +01:00
Dawa Ometto 18a16665cc Don't add base path to file url for deletion. Resolves https://github.com/gollum/gollum/pull/1620 (#1639)
* Add regression test
2021-01-04 11:29:45 +01:00
Bart Kamphorst 6c0796733d Filter _Template content. Resolves #1603 and #1640. (#1612)
* Filter _Template content. Resolves #1603 and #1640.

* Introduces a Gollum::TemplateFilter class with methods to easily add _Template Filters.

* Adds support for relative _Template pages with fallback to _Template in root.
2020-12-26 14:10:02 +01:00
Nikita Ivanov 97ed5a7c57 Add tests to the ? in page name fix (#1641) 2020-12-14 11:24:50 +01:00
Nikita Ivanov c87c8c446e Fix bug when link to a page doesn't work if it has ? in its title (#1634) 2020-12-13 13:42:19 +01:00
Dawa Ometto bfbfb21c17 Release 5.1.2 2020-12-01 22:22:22 +01:00
Dawa Ometto 137728cdab Guard against malicious file names 2020-12-01 22:21:54 +01:00
Dawa Ometto 3f7fd21d4a Update kramdown-parse-gfm (#1623) 2020-11-19 17:10:37 +01:00
Dawa Ometto 2204cef0ef Fix dependency status badge 2020-11-02 22:14:51 +01:00
Dawa Ometto 29ad7127d1 Add dependency status badge 2020-11-02 22:14:11 +01:00
Dawa Ometto 123518b940 Don't escape page path. Resolves #1615 (#1618) 2020-09-21 15:50:58 +02:00
Dawa Ometto dbe849707a Update GPG key 2020-09-13 16:21:43 +02:00
Dawa Ometto 986a76cf8e Release 5.1.1 2020-08-11 12:57:42 +02:00
105 changed files with 2434 additions and 787 deletions
+50
View File
@@ -0,0 +1,50 @@
name: Deploy docker
on:
push:
branches:
- master
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set deploy name to master
if: github.ref == 'refs/heads/master'
run: |
echo "DEPLOY_NAME=gollumwiki/gollum:master" >> $GITHUB_ENV
- name: Set deploy name to release version
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "DEPLOY_NAME=gollumwiki/gollum:${{ github.ref_name }}" >> $GITHUB_ENV
- name: Check Out Repo
uses: actions/checkout@v2
- name: Login
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: ./
file: ./Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: ${{ env.DEPLOY_NAME }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
platforms: linux/amd64, linux/arm64
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
+38
View File
@@ -0,0 +1,38 @@
name: Build and Test Docker
on: [push, pull_request]
env:
CI_IMAGE: gollum-ci-img
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v2
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build
id: docker_build
uses: docker/build-push-action@v2
with:
context: ./
file: ./Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: false
tags: ${{ env.CI_IMAGE }}
outputs: type=docker
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: docker state
run: docker image ls
- name: Run gollum as test
run: docker run -e CI=true ${{ env.CI_IMAGE }} --irb
+26
View File
@@ -0,0 +1,26 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref_name }}
release_name: Release ${{ github.ref_name }}
draft: true
prerelease: false
body_path: "LATEST_CHANGES.md"
+46
View File
@@ -0,0 +1,46 @@
name: Ruby Build
on: [push, pull_request]
jobs:
jruby_build:
name: JRuby (${{ matrix.ruby }})
runs-on: ubuntu-latest
strategy:
matrix:
ruby: [jruby-9.3.2.0]
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- name: Set up Java
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rake
mri_build:
name: Ruby (${{ matrix.ruby }})
runs-on: ubuntu-latest
strategy:
matrix:
ruby: ['2.6', '2.7', '3.0', '3.1']
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rake
+1
View File
@@ -8,3 +8,4 @@ Gemfile.lock
.*
!.sprockets*
!lib/gollum/public/gollum/stylesheets/_styles.css
!.github*
-9
View File
@@ -1,9 +0,0 @@
rvm:
- 2.4.0
- 2.6.0
- jruby-9.2.9.0
jdk:
- oraclejdk9
before_install:
- sudo apt-get update
- sudo apt-get install libicu-dev
+4 -2
View File
@@ -21,7 +21,7 @@ Before submitting an issue, **please carefully look through the following places
Security vulnerabilities can be reported directly to the maintainers using these GPG keys:
* [@dometto](https://pgp.mit.edu/pks/lookup?op=vindex&search=0xD637E455CD3E27BF)
* [@dometto](https://keys.openpgp.org/vks/v1/by-fingerprint/02354CC9F820B52CC2791979BB8CCC95FD83B795)
Lastly, please **consider helping out** by opening a Pull Request!
@@ -62,6 +62,8 @@ Pull Requests fixing bugs, implementing new features, or updating documentation
```
bundle exec rake test
```
To profile slow tests, you can use `bundle exec rake test TESTOPTS="--verbose"`.
### Working with test repositories
@@ -70,7 +72,7 @@ An example of how to add a test file to the bare repository lotr.git.
```
mkdir tmp
cd tmp
git clone ../lotr.git/
git clone ../test/examples/lotr.git/
git log
echo "test" > test.md
git add .
+41
View File
@@ -0,0 +1,41 @@
FROM ruby:2.7-alpine AS builder
RUN apk add \
build-base \
cmake \
git \
icu-dev \
openssl-dev
COPY Gemfile* /tmp/
COPY gollum.gemspec* /tmp/
WORKDIR /tmp
RUN bundle install
RUN gem install \
asciidoctor \
creole \
wikicloth \
org-ruby \
RedCloth \
bibtex-ruby \
&& echo "gem-extra complete"
WORKDIR /app
COPY . /app
RUN bundle exec rake install
FROM ruby:2.7-alpine
COPY --from=builder /usr/local/bundle/ /usr/local/bundle/
RUN apk add \
bash \
git \
libc6-compat
VOLUME /wiki
WORKDIR /wiki
COPY docker-run.sh /docker-run.sh
ENTRYPOINT ["/docker-run.sh"]
+18 -5
View File
@@ -1,10 +1,23 @@
source 'https://rubygems.org'
if RUBY_PLATFORM == 'java'
gem 'warbler'
end
gem 'warbler', platforms: :jruby
# FIXME:
#
# There's an issue in 1.12.5 that causes XHTML elements to be generated badly,
# causing Gollum's test suite to fail.[1] The issue has been fixed upstream,
# but we're still waiting for a new Nokogiri point release.
#
# However, 1.12.5 is a security patch, so we don't want end users to use an
# older version of Nokogiri. But this is safe to do in our CI environment.
#
# Once there's a new Nokogiri release, we can remove this dependency and JRuby
# CI should pass normally again.
#
# Note that Nokogiri 1.11+ does not support Ruby v2.4.x anymore. So to make our
# current CI workflows pass, we should only try to install this version of
# Nokogiri for newer Ruby versions.
gemspec
gem "rake", '~> 12.3', '>= 12.3.3'
gem 'rake', '~> 13.0'
+32
View File
@@ -1,3 +1,35 @@
# 5.3.0 / 2022-05-25
* Feature: allow for overriding only specific Mustache templates/partials (@beporter)
* Feature: Add option to show browser's local time (@NikitaIvanovV)
* Improvement: presentation on mobile devises (@benjaminwil)
* Improvement: Add page context to template filter. #1603 (@tevino)
* Fix: restore normalize check on file upload (@manofstick)
* Fix mathjax on edit and create pages. #1772 (@fhchl)
* Fix utf-8 issues: #1721 #1758 #1801 (@basking2, @dometto)
* Fix an IME rendering issue. #1735 (@yy0931)
* Fix broken history button when viewing historical deleted file. (@NikitaIvanovV)
* Fix: non-ascii characters in page names are not rendered correctly in the preview tab of the "Edit" page. #1739 (@yy0931)
* Fix: anchors and header display on JRuby. #1779
# 5.2.3 / 2021-04-18
* Fix bug preventing page titles from being displayed
# 5.2.1 / 2021-02-25
* Fix include call to a missing asset (@benjaminwil). This caused slow first page loads on JRuby.
# 5.2 / 2021-02-24
* Improved styling and Primer upgrade (@benjaminwil)
* Add redirect to rename commit (@ViChyavIn)
* Updated dependencies
* Bugfixes
# 5.1.2
* Guard against malicious filenames in breadcrumbs
# 5.1
* Bugfixes
+13
View File
@@ -0,0 +1,13 @@
# 5.3.0 / 2022-05-24
* Feature: allow for overriding only specific Mustache templates/partials (@beporter)
* Feature: Add option to show browser's local time (@NikitaIvanovV)
* Improvement: presentation on mobile devises (@benjaminwil)
* Improvement: Add page context to template filter. #1603 (@tevino)
* Fix: restore normalize check on file upload (@manofstick)
* Fix mathjax on edit and create pages. #1772 (@fhchl)
* Fix utf-8 issues: #1721 #1758 #1801 (@basking2, @dometto)
* Fix an IME rendering issue. #1735 (@yy0931)
* Fix broken history button when viewing historical deleted file. (@NikitaIvanovV)
* Fix: non-ascii characters in page names are not rendered correctly in the preview tab of the "Edit" page. #1739 (@yy0931)
* Fix: anchors and header display on JRuby. #1779
+39 -26
View File
@@ -2,22 +2,27 @@ gollum -- A git-based Wiki
====================================
[![Gem Version](https://badge.fury.io/rb/gollum.svg)](http://badge.fury.io/rb/gollum)
[![Build Status](https://travis-ci.org/gollum/gollum.svg?branch=master)](https://travis-ci.org/gollum/gollum)
![Build Status](https://github.com/gollum/gollum/actions/workflows/test.yaml/badge.svg)
[![Open Source Helpers](https://www.codetriage.com/gollum/gollum/badges/users.svg)](https://www.codetriage.com/gollum/gollum)
[![Cutting Edge Dependency Status](https://dometto-cuttingedge.herokuapp.com/github/gollum/gollum/svg 'Cutting Edge Dependency Status')](https://dometto-cuttingedge.herokuapp.com/github/gollum/gollum/info)
[![Docker Pulls](https://img.shields.io/docker/pulls/gollumwiki/gollum)](https://hub.docker.com/r/gollumwiki/gollum)
**Gollum version 5.0 is out!** See [here](https://github.com/gollum/gollum/wiki/5.0-release-notes) for a list of changes and new features compared to Gollum version 4.x, and see some [Screenshots](https://github.com/gollum/gollum/wiki/Screenshots) of Gollum's features.
See the [wiki](https://github.com/gollum/gollum/wiki) for extensive documentation, along with [screenshots](https://github.com/gollum/gollum/wiki/Screenshots) of Gollum's features.
## DESCRIPTION
Gollum is a simple wiki system built on top of Git. A Gollum Wiki is simply a git repository of a specific nature:
* A Gollum repository's contents are human-editable text or markup files.
* Pages may be organized into directories any way you choose.
* Other content can also be included, for example images, PDFs and headers/footers for your pages.
* Gollum pages:
* May be written in a variety of [markups](#markups).
* Can be edited with your favourite system editor or IDE (changes will be visible after committing) or with the built-in web interface.
* Can be edited with your favourite editor (changes will be visible after committing) or with the built-in web interface.
* Can be displayed in all versions, reverted, etc.
* Gollum strives to be compatible with GitHub wikis (see `--lenient-tag-lookup`)
* Gollum strives to be [compatible](https://github.com/gollum/gollum/wiki/5.0-release-notes#compatibility-option) with [GitHub](https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis) and [GitLab](https://docs.gitlab.com/ee/user/project/wiki/#create-or-edit-wiki-pages-locally) wikis.
* Just clone your GitHub/GitLab wiki and view and edit it locally!
* Gollum supports advanced functionality like:
* [UML diagrams](https://github.com/gollum/gollum/wiki#plantuml-diagrams)
* [BibTeX and Citation support](https://github.com/gollum/gollum/wiki/BibTeX-and-Citations)
@@ -30,41 +35,48 @@ Gollum is a simple wiki system built on top of Git. A Gollum Wiki is simply a gi
### SYSTEM REQUIREMENTS
Gollum runs on Unix-like systems using its [adapter](https://github.com/gollum/rugged_adapter) for [rugged](https://github.com/libgit2/rugged) by default. You can also run Gollum on [JRuby](https://github.com/jruby/jruby) via its [adapter](https://github.com/repotag/gollum-lib_rjgit_adapter) for [RJGit](https://github.com/repotag/rjgit/). On Windows, Gollum runs only on JRuby.
Gollum runs on Unix-like systems using its default [adapter](https://github.com/gollum/rugged_adapter) for [rugged](https://github.com/libgit2/rugged). You can also run Gollum on [JRuby](https://github.com/jruby/jruby) via its [adapter](https://github.com/repotag/gollum-lib_rjgit_adapter) for [RJGit](https://github.com/repotag/rjgit/). On Windows, Gollum runs only on JRuby.
## INSTALLATION
1. Ruby is best installed either via [RVM](https://rvm.io/) or a package manager of choice.
2. Gollum is best installed via RubyGems:
### As a Ruby Gem
Ruby is best installed either via [RVM](https://rvm.io/) or a package manager of choice. Then simply:
```
[sudo] gem install gollum
gem install gollum
```
Installation examples for individual systems can be seen [here](https://github.com/gollum/gollum/wiki/Installation).
To run, simply:
1. Run: `gollum /path/to/wiki`.
1. Run: `gollum /path/to/wiki` where `/path/to/wiki` is an initialized Git repository.
2. Open `http://localhost:4567` in your browser.
### Via Docker
See [here](https://github.com/gollum/gollum/wiki/Gollum-via-Docker) for instructions on how to run Gollum via Docker.
### Misc
See [below](#running-from-source) for information on running Gollum from source, as a Rack app, and more.
### Markups
## MARKUPS
Gollum allows using different markup languages on different wiki pages. It presently ships with support for the following markups:
* [Markdown](http://daringfireball.net/projects/markdown/syntax) (see [below](#Markdown-flavors) for more information on Markdown flavors)
* [RDoc](http://rdoc.sourceforge.net/)
You can easily activate support for other markups by installing additional renderers (any that are supported by [github-markup](https://github.com/github/markup)):
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `[sudo] gem install asciidoctor`
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `[sudo] gem install creole`
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `[sudo] gem install wikicloth`
* [Org](http://orgmode.org/worg/dev/org-syntax.html) -- `[sudo] gem install org-ruby`
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `gem install asciidoctor`
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `gem install creole`
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `gem install wikicloth`
* [Org](http://orgmode.org/worg/dev/org-syntax.html) -- `gem install org-ruby`
* [Pod](http://perldoc.perl.org/perlpod.html) -- requires Perl >= 5.10 (the `perl` command must be available on your command line)
* Lower versions should install `Pod::Simple` from CPAN.
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 2 (the `python2` command must be available on your command line)
* Note that Gollum will also need you to install `docutils` for your Python 2.
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `[sudo] gem install RedCloth`
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 3
* Note that Gollum will also need you to install `docutils` for python
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `gem install RedCloth`
### Markdown flavors
@@ -82,19 +94,15 @@ See [here](https://github.com/gollum/gollum/wiki/Custom-rendering-gems) for inst
### Rack
Gollum can also be ran with any [rack-compatible web server](https://github.com/rack/rack#supported-web-servers). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack).
Gollum can also be run with any [rack-compatible web server](https://github.com/rack/rack#supported-web-servers). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack).
### Rack, with an authentication server
Gollum can also be ran alongside a CAS (Central Authentication Service) SSO (single sign-on) server. With a bit of tweaking, this adds basic user-support to Gollum. To see an example and an explanation, navigate [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack-and-CAS-SSO).
### Docker
Gollum can also be ran via [Docker](https://www.docker.com/). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Docker).
Gollum can also be run alongside a CAS (Central Authentication Service) SSO (single sign-on) server. With a bit of tweaking, this adds basic user-support to Gollum. To see an example and an explanation, navigate [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack-and-CAS-SSO).
### Service
Gollum can also be ran as a service. More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-as-a-service).
Gollum can also be run as a service. More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-as-a-service).
## CONFIGURATION
@@ -104,7 +112,7 @@ Gollum comes with the following command line options:
| ----------------- | --------- | ----------- |
| --host | [HOST] | Specify the hostname or IP address to listen on. Default: '0.0.0.0'.<sup>1</sup> |
| --port | [PORT] | Specify the port to bind Gollum with. Default: `4567`. |
| --config | [FILE] | Specify path to Gollum's configuration file. |
| --config | [FILE] | Specify path to Gollum's [configuration file](#Config-file). |
| --ref | [REF] | Specify the git branch to serve. Default: `master`. |
| --bare | none | Tell Gollum that the git repository should be treated as bare. |
| --adapter | [ADAPTER] | Launch Gollum using a specific git adapter. Default: `rugged`.<sup>2</sup> |
@@ -115,6 +123,7 @@ Gollum comes with the following command line options:
| --css | none | Tell Gollum to inject custom CSS into each page. Uses `custom.css` from wiki root.<sup>3</sup> |
| --js | none | Tell Gollum to inject custom JS into each page. Uses `custom.js` from wiki root.<sup>3</sup> |
| --no-edit | none | Disable the feature of editing pages. |
| --local-time | none | Use the browser's local timezone instead of the server's for displaying dates. Default: false.
| --follow-renames, --no-follow-renames | none | Follow pages across renames in the History view. Default: true.
| --allow-uploads | [MODE] | Enable file uploads. If set to `dir`, Gollum will store all uploads in the `/uploads/` directory in repository root. If set to `page`, Gollum will store each upload at the currently edited page.<sup>4</sup> |
| --mathjax | none | Enables MathJax (renders mathematical equations). By default, uses the `TeX-AMS-MML_HTMLorMML` config with the `autoload-all` extension.<sup>5</sup> |
@@ -124,7 +133,7 @@ Gollum comes with the following command line options:
| --no-display-metadata | none | Do not render metadata tables in pages. |
| --user-icons | [MODE] | Tell Gollum to use specific user icons for history view. Can be set to `gravatar`, `identicon` or `none`. Default: `none`. |
| --mathjax-config | [FILE] | Specify path to a custom MathJax configuration. If not specified, uses the `mathjax.config.js` file from repository root. |
| --template-dir | [PATH] | Specify custom mustache template directory. |
| --template-dir | [PATH] | Specify custom mustache template directory. Only overrides templates that exist in this directory. |
| --template-page | none | Use _Template in root as a template for new pages. Must be committed. |
| --emoji | none | Parse and interpret emoji tags (e.g. `:heart:`) except when the leading colon is backslashed (e.g. `\:heart:`). |
| --lenient-tag-lookup | none | Internal links resolve case-insensitively, will treat spaces as hyphens, and will match the first page found with a certain filename, anywhere in the repository. Provides compatibility with Gollum 4.x. |
@@ -148,3 +157,7 @@ When `--config` option is used, certain inner parts of Gollum can be customized.
## CONTRIBUTING
Please consider helping out! See [CONTRIBUTING](CONTRIBUTING.md) for information on how to submit issues, and how to start hacking on gollum.
## THANKS TO
[![Testing Powered By SauceLabs](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-gray.png?sanitize=true "Testing Powered By SauceLabs")](https://saucelabs.com)
+55 -12
View File
@@ -1,6 +1,7 @@
require 'rubygems'
require 'rake'
require 'date'
require 'tempfile'
#############################################################################
#
@@ -8,6 +9,10 @@ require 'date'
#
#############################################################################
def date
Time.now.strftime("%Y-%m-%d")
end
def name
@name ||= Dir['*.gemspec'].first.split('.').first
end
@@ -17,6 +22,14 @@ def version
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
end
def latest_changes_file
'LATEST_CHANGES.md'
end
def history_file
'HISTORY.md'
end
# assumes x.y.z all digit version
def next_version
# x.y.z
@@ -38,14 +51,6 @@ def bump_version
new_version
end
def date
Date.today.to_s
end
def rubyforge_project
name
end
def gemspec_file
"#{name}.gemspec"
end
@@ -114,6 +119,7 @@ task :release => :build do
puts "You must be on the master branch to release!"
exit!
end
Rake::Task[:changelog].execute
sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git pull --rebase origin master"
sh "git tag v#{version}"
@@ -143,12 +149,9 @@ task :gemspec => :validate do
spec = File.read(gemspec_file)
head, manifest, tail = spec.split(" # = MANIFEST =\n")
# replace name version and date
# replace name and version
replace_header(head, :name)
replace_header(head, :version)
replace_header(head, :date)
#comment this out if your rubyforge_project has a different name
replace_header(head, :rubyforge_project)
# determine file list from git ls-files
files = `git ls-files`.
@@ -179,6 +182,46 @@ task :validate do
end
end
desc 'Build changlog'
task :changelog do
[latest_changes_file, history_file].each do |f|
unless File.exists?(f)
puts "#{f} does not exist but is required to build a new release."
exit!
end
end
latest_changes = File.open(latest_changes_file)
version_pattern = "# #{version}"
if !`grep "#{version_pattern}" #{history_file}`.empty?
puts "#{version} is already described in #{history_file}"
exit!
end
begin
unless latest_changes.readline.chomp! =~ %r{#{version_pattern}}
puts "#{latest_changes_file} should begin with '#{version_pattern}'"
exit!
end
rescue EOFError
puts "#{latest_changes_file} is empty!"
exit!
end
body = latest_changes.read
body.scan(/\s*#\s+\d\.\d.*/) do |match|
puts "#{latest_changes_file} may not contain multiple markdown headers!"
exit!
end
temp = Tempfile.new
temp.puts("#{version_pattern} / #{date}\n#{body}\n")
temp.close
`cat #{history_file} >> #{temp.path}`
`cat #{temp.path} > #{history_file}`
end
desc 'Precompile assets'
task :precompile do
require './lib/gollum/app.rb'
+7 -5
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env ruby
#!/usr/bin/env -S ruby -Eutf-8
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
@@ -112,6 +112,9 @@ MSG
opts.on('--no-edit', 'Disable the feature of editing pages.') do
wiki_options[:allow_editing] = false
end
opts.on('--local-time', "Use the browser's local timezone instead of the server's for displaying dates.") do
wiki_options[:show_local_time] = true
end
opts.on('--follow-renames', 'Follow pages across renames in the History view. Default: true.') do
wiki_options[:follow_renames] = true
end
@@ -149,10 +152,10 @@ MSG
'Can be set to \'gravatar\' or \'identicon\'. Default: standard avatar.') do |mode|
wiki_options[:user_icons] = mode.to_s
end
opts.on('--template-dir [PATH]', 'Specify custom mustache template directory.') do |path|
opts.on('--template-dir [PATH]', 'Specify custom mustache template directory. Only overrides templates that exist in this directory.') do |path|
wiki_options[:template_dir] = path
end
opts.on('--template-page', 'Use _Template in root as a template for new pages.') do
opts.on('--template-page', 'Use _Template.{ext} as a template for new pages.') do
wiki_options[:template_page] = true
end
opts.on('--lenient-tag-lookup', 'Internal links resolve case-insensitively, will treat spaces as hyphens, and will match the first page found with a certain filename, anywhere in the repository. Provides compatibility with Gollum 4.x.') do
@@ -206,7 +209,7 @@ begin
rescue OptionParser::InvalidOption => e
puts "gollum: #{e.message}"
puts 'gollum: try \'gollum --help\' for more information'
exit
exit 1
end
# --gollum-path wins over ARGV[0]
@@ -271,7 +274,6 @@ else
Precious::App.set(:environment, ENV.fetch('RACK_ENV', :production).to_sym)
Precious::App.set(:gollum_path, gollum_path)
Precious::App.set(:wiki_options, wiki_options)
Precious::App.settings.mustache[:templates] = wiki_options[:template_dir] if wiki_options[:template_dir]
if cfg = options[:config]
# If the path begins with a '/' it will be considered an absolute path,
+40 -28
View File
@@ -11,8 +11,11 @@ migrate_options = {
:hyphenate => true
}
def setting(const)
Object.const_defined?(const.upcase) && Object.const_get(const.upcase)
def setting(variable_name)
class_variable_name = :"@@#{variable_name.to_s}"
Object.class_variable_defined?(class_variable_name) &&
Object.class_variable_get(class_variable_name)
end
opts = OptionParser.new do |opts|
@@ -25,7 +28,7 @@ It finds and repairs Gollum link tags that no longer work under 5.x for three re
* 5.x wiki internal links are no longer 'global'.
* NB: you can use the --lenient-tag-lookup option in gollum >= 5.x to enable 4.x-backwards compatible tags.
See https://github.com/gollum/gollum/wiki/5.0-release-notes#filename-handling for more information.
Usage of this script comes without any warranty.
@@ -38,7 +41,7 @@ You can use the --page-file-dir and --config options as you would normally with
Requires a non-bare repository. Recommended usage:
1. Clone your wiki's repository to create a backup.
2. Run this script on your cloned repo.
2. Run this script on your cloned repo.
3. If all looks sane, run the script with the --write option. This will overwrite files in your working directory, but not commit the changes, so you have time to review them.
4. Do a 'git diff' to inspect the changes.
5. Commit the changes if all looks sane, and push/pull them back into your original repo.
@@ -52,23 +55,23 @@ EOF
opts.on('--page-file-dir [PATH]', 'Specify the subdirectory for all pages. Default: repository root.') do |path|
wiki_options[:page_file_dir] = path
end
opts.on('--prefer-relative-links', 'When specified, will try to replace broken links with relative links (\'[[Foo/Bar]]\' instead of \'[[/Subdir/Foo/Bar]]\') where possible.') do
migrate_options[:prefer_relative] = true
end
opts.on('--hyphenate', 'Default. Repair links that use spaces instead of hyphens: [[Bilbo Baggins]] -> [[Bilbo-Baggins]]') do
migrate_options[:hyphenate] = true
end
opts.on('--no-hyphenate', 'Turn off the --hyphenate option.') do
migrate_options[:hyphenate] = false
end
opts.on('--run-silent', 'Don\'t output anything.') do
migrate_options[:run_silent] = true
end
opts.on('--write', 'No dry run: actually perform the substitutions.') do
migrate_options[:no_dry_run] = true
end
@@ -78,8 +81,11 @@ end
begin
opts.parse!
migrate_options.each do |setting, value|
const = setting.to_s.upcase
Object.const_set(const, value) unless Object.const_defined?(const)
variable_name = :"@@#{setting.to_s}"
unless Object.class_variable_defined?(variable_name)
Object.class_variable_set(variable_name, value)
end
end
wiki_options[:page_file_dir] = setting(:page_file_dir) ? setting(:page_file_dir) : wiki_options[:page_file_dir] # Allow settings :page_file_dir through PAGE_FILE_DIR constant.
rescue OptionParser::InvalidOption
@@ -88,7 +94,7 @@ rescue OptionParser::InvalidOption
exit
end
REPO = ARGV[0] || Dir.pwd
wiki_directory = ARGV[0] || Dir.pwd
require 'gollum-lib'
@@ -98,7 +104,7 @@ if cfg = options[:config]
cfg = File.join(Dir.getwd, cfg) unless cfg.slice(0) == File::SEPARATOR
require cfg
end
class Gollum::Filter::CodeMigrator < Gollum::Filter::Code
def extract(data)
case @markup.format
@@ -126,7 +132,7 @@ class Gollum::Filter::CodeMigrator < Gollum::Filter::Code
next '' if m_end.length < m_start.length
lang = m_lang ? m_lang.strip.split.first : nil
cache_codeblock($~.to_s)
end
end
end
data.gsub!(/^([ ]{0,3})``` ?([^\r\n]+)?\r?\n(.+?)\r?\n[ ]{0,3}```[ \t]*\r?$/m) do
@@ -134,7 +140,7 @@ class Gollum::Filter::CodeMigrator < Gollum::Filter::Code
end
data
end
def process(data)
return data if data.nil? || data.size.zero? || @map.size.zero?
@map.each do |id, block| ## Just put the code blocks back in verbatim
@@ -142,23 +148,23 @@ class Gollum::Filter::CodeMigrator < Gollum::Filter::Code
end
data
end
def cache_codeblock(block)
id = "#{open_pattern}#{Digest::SHA1.hexdigest(block)}#{close_pattern}"
@map[id] = block
id
end
end
class ::Gollum::Filter::TagMigrator < Gollum::Filter::Tags
def process_tag(tag)
link_part, extra = parse_tag_parts(tag)
orig_tag = %{[[#{tag}]]}
return orig_tag if link_part.nil?
img_args = extra ? [extra, link_part] : [link_part]
mime = MIME::Types.type_for(::File.extname(img_args.first.to_s)).first
# For any kind of tag other than an internal link: just return the tag.
if tag =~ /^_TOC_/ || link_part =~ /^_$/ || link_part =~ /^#{INCLUDE_TAG}/ || (mime && mime.content_type =~ /^image/) || process_external_link_tag(link_part, extra) || process_file_link_tag(link_part, extra)
return orig_tag
@@ -168,7 +174,7 @@ class ::Gollum::Filter::TagMigrator < Gollum::Filter::Tags
link = link_part
page = find_page_or_file_from_path(link)
anchor = nil
if page.nil? # No match yet, now try finding the page with anchor removed
if pos = link.rindex('#')
anchor = link[pos..-1]
@@ -203,12 +209,12 @@ class ::Gollum::Filter::TagMigrator < Gollum::Filter::Tags
pick = possibles.first
return tag_for_pick(pick, orig_tag, extra, anchor, @markup.page.path)
end
end
end
private
def tag_for_pick(pick, orig_tag, extra, anchor, linking_page_path)
pick = if setting(:prefer_relative)
overlapping_path = Pathname.new(linking_page_path).dirname.to_s
@@ -220,7 +226,7 @@ class ::Gollum::Filter::TagMigrator < Gollum::Filter::Tags
end
new_tag = extra.nil? ? %{[[#{pick}#{anchor}]]} : %{[[#{extra}|#{pick}#{anchor}]]}
log(:info, "#{@markup.page.path}: Changing #{orig_tag} -> #{new_tag}")
new_tag
new_tag
end
end
@@ -232,8 +238,12 @@ end
filter_chain = [:PlainTextMigrator, :CodeMigrator, :TagMigrator]
wiki = ::Gollum::Wiki.new(REPO, wiki_options.merge({:filter_chain => filter_chain}))
TREE = wiki.tree_list(wiki.ref, true, true).map {|file| ::File.join('/', file.path)}
wiki = ::Gollum::Wiki.new(wiki_directory, wiki_options.merge({:filter_chain => filter_chain}))
Object.class_variable_set(
:"@@wiki_tree",
wiki.tree_list(wiki.ref, true, true).map {|file| ::File.join('/', file.path)}
)
def find_linked(link)
link.gsub!(' ', '-') if setting(:hyphenate) # Match paths containing dashes instead of spaces
@@ -243,7 +253,9 @@ def find_linked(link)
test_path = ::File.extname(link).empty? ? /#{link}\..+/ : link
# Select pages from the wiki whose path =~ 'Foo/Bar/Samwi.*'
# Match case-insenstively to mimic 4.x behavior!
TREE.select {|path| path =~ /^\/(.*\/)?#{test_path}/i}
Object.class_variable_get(:"@@wiki_tree").select { |path|
path =~ /^\/(.*\/)?#{test_path}/i
}
end
def log(kind, msg = nil)
@@ -268,4 +280,4 @@ wiki.pages.each do |page|
f.close
end
log(:none, '====')
end
end
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env ruby
#
# Distributed under the terms of the MIT License.
#
# Author: Sam Baskinger <basking2@yahoo.com>
#
# Description: gollum-post is an example script that shows how
# to post a file to Gollum. This may be used
# to build scripts around CI/CD pipelines that
# publish their documentation to Gollum.
#
require 'uri'
require 'mechanize'
require 'digest'
GOLLUM=URI('https://mygollum.server')
m = Mechanize.new()
page="TestPage"
path="/automated/docs"
format="asciidoc"
content="""
= This is #{page}
This page is automatically generated.
"""
message='Posting current documentation.'
# Check if the page exists.
p = m.get("#{GOLLUM}#{path}/#{page}")
# If we were redirected to the creat page...
if p.uri.to_s =~ /\/gollum\/create/
# ... then create the page.
p = m.post("#{GOLLUM}/gollum/create",
'keybinding' => 'default',
'page' => page,
'path' => path,
'format' => 'asciidoc',
'message' => 'Publish bot.',
'content' => content)
else
# ... else, get the previous content and update it.
p = m.get("#{GOLLUM}/gollum/edit#{path}/#{page}")
# Get the previous content. You _could_ check if this is unchanged at this
# step and post nothing.
previous_content = p.xpath('//textarea[@id="gollum-editor-body"]')[0].text
# The previous ETag is the Git SHA-1. We need this to replace the previous contents.
prev_etag = Digest::SHA1.hexdigest("blob #{previous_content.length}\0#{previous_content}")
# Post the updated document using the ETag of the previous document to avoid collisions.
p = m.post("#{GOLLUM}/gollum/edit#{path}/#{page}",
'keybinding' => 'default',
'page' => page,
'path' => path,
'format' => 'asciidoc',
'message' => message,
'etag' => prev_etag,
'content' => content)
end
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# Initialize the wiki
if [ ! -d .git ]; then
git init
fi
# Set git user.name and user.email
if [ ${GOLLUM_AUTHOR_USERNAME:+1} ]; then
git config user.name "${GOLLUM_AUTHOR_USERNAME}"
fi
if [ ${GOLLUM_AUTHOR_EMAIL:+1} ]; then
git config user.email "${GOLLUM_AUTHOR_EMAIL}"
fi
# Start gollum service
[[ "$@" != *--mathjax* ]] && echo "WARNING: Mathjax will soon be disabled by default. To explicitly enable it, use --mathjax" >&2
exec gollum $@ --mathjax
+30 -22
View File
@@ -1,12 +1,9 @@
Gem::Specification.new do |s|
s.specification_version = 2 if s.respond_to? :specification_version=
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
s.rubygems_version = '1.3.5'
s.required_ruby_version = '>= 1.9'
s.required_ruby_version = '>= 2.6'
s.name = 'gollum'
s.version = '5.1'
s.date = '2020-08-03'
s.version = '5.3.0'
s.license = 'MIT'
s.summary = 'A simple, Git-powered wiki.'
@@ -23,35 +20,38 @@ Gem::Specification.new do |s|
s.rdoc_options = ['--charset=UTF-8']
s.extra_rdoc_files = %w[README.md LICENSE]
s.add_dependency 'gollum-lib', '~> 5.0'
s.add_dependency 'kramdown', '~> 2.1.0'
s.add_dependency 'kramdown-parser-gfm', '~> 1.0.0'
s.add_dependency 'rdoc', '~> 6'
s.add_dependency 'gollum-lib', '~> 5.1'
s.add_dependency 'kramdown', '~> 2.3'
s.add_dependency 'kramdown-parser-gfm', '~> 1.1.0'
s.add_dependency 'sinatra', '~> 2.0'
s.add_dependency 'sinatra-contrib', '~> 2.0'
s.add_dependency 'mustache', ['>= 0.99.5', '< 1.0.0']
s.add_dependency 'mustache-sinatra', '>= 1.0.1', '< 2'
s.add_dependency 'useragent', '~> 0.16.2'
s.add_dependency 'gemojione', '~> 4.1'
s.add_dependency 'octicons', '~> 8.5'
s.add_dependency 'octicons', '~> 12.0'
s.add_dependency 'sprockets', '~> 3.7'
s.add_dependency 'sass', '~> 3.5'
s.add_dependency 'uglifier', '~> 3.2'
s.add_dependency 'uglifier', '~> 4.2'
s.add_dependency 'sprockets-helpers', '~> 1.2'
s.add_dependency 'rss', '~> 0.2.9'
s.add_dependency 'therubyrhino', '~> 2.1.0'
s.add_dependency 'webrick', '~> 1.7'
s.add_dependency 'i18n', '~> 1.8'
s.add_development_dependency 'rack-test', '~> 0.6.3'
s.add_development_dependency 'shoulda', '~> 3.6.0'
s.add_development_dependency 'minitest-reporters', '~> 1.3.6'
s.add_development_dependency 'twitter_cldr', '~> 3.2.0'
s.add_development_dependency 'mocha', '~> 1.8.0'
s.add_development_dependency 'test-unit', '~> 3.3.0'
s.add_development_dependency 'webrick', '~> 1.4.2'
# = MANIFEST =
s.files = %w[
CONTRIBUTING.md
Dockerfile
Gemfile
HISTORY.md
LATEST_CHANGES.md
LICENSE
README.md
Rakefile
@@ -59,24 +59,26 @@ Gem::Specification.new do |s|
bin/gollum-migrate-tags
config.rb
config.ru
contrib/automation/gollum-post
contrib/openrc/conf.d/gollum
contrib/openrc/init.d/gollum
contrib/systemd/gollum@.service
contrib/sysv-debian/init.d/gollum
docker-run.sh
gollum.gemspec
lib/gollum.rb
lib/gollum/app.rb
lib/gollum/assets.rb
lib/gollum/helpers.rb
lib/gollum/public/assets/.sprockets-manifest-459226ba5fc211b78ba9a3aa6ebde96c.json
lib/gollum/public/assets/app-6e925e38a12a40c4fa9e0400cc874e0f4f97b66fdeb90a144dea527dbb544fbd.js
lib/gollum/public/assets/app-6e925e38a12a40c4fa9e0400cc874e0f4f97b66fdeb90a144dea527dbb544fbd.js.gz
lib/gollum/public/assets/app-b205e593a30f1cc0054e2e9ed9fc8af3658d8ef4a62b9708c20f204560deefb7.css
lib/gollum/public/assets/app-b205e593a30f1cc0054e2e9ed9fc8af3658d8ef4a62b9708c20f204560deefb7.css.gz
lib/gollum/public/assets/.sprockets-manifest-160337b312f8e438181baac4aaa37319.json
lib/gollum/public/assets/app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css
lib/gollum/public/assets/app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css.gz
lib/gollum/public/assets/app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js
lib/gollum/public/assets/app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js.gz
lib/gollum/public/assets/criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css
lib/gollum/public/assets/criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css.gz
lib/gollum/public/assets/editor-b2c10f22ef6ca1e120956a2d11ff4ca19ac44d7e7240f5cc43ec949184d8b708.js
lib/gollum/public/assets/editor-b2c10f22ef6ca1e120956a2d11ff4ca19ac44d7e7240f5cc43ec949184d8b708.js.gz
lib/gollum/public/assets/editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js
lib/gollum/public/assets/editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js.gz
lib/gollum/public/assets/print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css
lib/gollum/public/assets/print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css.gz
lib/gollum/public/gollum/javascript/HOWTO_UPDATE_ACE.md
@@ -1151,6 +1153,7 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/javascript/ace/worker-xquery.js
lib/gollum/public/gollum/javascript/app.js
lib/gollum/public/gollum/javascript/clipboard.min.js
lib/gollum/public/gollum/javascript/date.min.js
lib/gollum/public/gollum/javascript/editor.js
lib/gollum/public/gollum/javascript/editor/gollum.editor.js
lib/gollum/public/gollum/javascript/editor/langs/asciidoc.js
@@ -1174,18 +1177,18 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/javascript/jquery-1.7.2.min.js
lib/gollum/public/gollum/javascript/jquery.resize.js
lib/gollum/public/gollum/javascript/mousetrap.min.js
lib/gollum/public/gollum/javascript/polyfills.js
lib/gollum/public/gollum/stylesheets/_base.scss
lib/gollum/public/gollum/stylesheets/_breakpoint.scss
lib/gollum/public/gollum/stylesheets/_component.scss
lib/gollum/public/gollum/stylesheets/_features.scss
lib/gollum/public/gollum/stylesheets/_layout.scss
lib/gollum/public/gollum/stylesheets/_spinners.scss
lib/gollum/public/gollum/stylesheets/app.scss
lib/gollum/public/gollum/stylesheets/criticmarkup.scss
lib/gollum/public/gollum/stylesheets/dialog.scss
lib/gollum/public/gollum/stylesheets/editor.scss
lib/gollum/public/gollum/stylesheets/emoji.scss
lib/gollum/public/gollum/stylesheets/primer.scss
lib/gollum/public/gollum/stylesheets/primer.css
lib/gollum/public/gollum/stylesheets/print.scss
lib/gollum/public/gollum/stylesheets/spinner.scss
lib/gollum/public/gollum/stylesheets/tables.scss
@@ -1202,12 +1205,14 @@ Gem::Specification.new do |s|
lib/gollum/templates/history_authors/none.mustache
lib/gollum/templates/latest_changes.mustache
lib/gollum/templates/layout.mustache
lib/gollum/templates/mobilenav.mustache
lib/gollum/templates/navbar.mustache
lib/gollum/templates/overview.mustache
lib/gollum/templates/page.mustache
lib/gollum/templates/pagination.mustache
lib/gollum/templates/search.mustache
lib/gollum/templates/searchbar.mustache
lib/gollum/templates/user.mustache
lib/gollum/templates/wiki_content.mustache
lib/gollum/uri_encode_component.rb
lib/gollum/views/commit.rb
@@ -1216,9 +1221,11 @@ Gem::Specification.new do |s|
lib/gollum/views/edit.rb
lib/gollum/views/editable.rb
lib/gollum/views/error.rb
lib/gollum/views/has_math.rb
lib/gollum/views/has_page.rb
lib/gollum/views/has_user_icons.rb
lib/gollum/views/helpers.rb
lib/gollum/views/helpers/locale_helpers.rb
lib/gollum/views/history.rb
lib/gollum/views/latest_changes.rb
lib/gollum/views/layout.rb
@@ -1227,6 +1234,7 @@ Gem::Specification.new do |s|
lib/gollum/views/pagination.rb
lib/gollum/views/rss.rb
lib/gollum/views/search.rb
lib/gollum/views/template_cascade.rb
licenses/licenses.txt
]
# = MANIFEST =
+17 -13
View File
@@ -5,32 +5,36 @@ require 'digest/sha1'
require 'ostruct'
# external
require 'i18n'
require 'github/markup'
require 'rhino' if RUBY_PLATFORM == 'java'
# internal
require File.expand_path('../gollum/uri_encode_component', __FILE__)
require ::File.expand_path('../gollum/uri_encode_component', __FILE__)
module Gollum
VERSION = '5.1'
VERSION = '5.3.0'
::I18n.available_locales = [:en]
::I18n.load_path = Dir[::File.expand_path("lib/gollum/locales") + "/*.yml"]
def self.assets_path
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
end
class Error < StandardError;
end
class TemplateFilter
@@filters = {}
class DuplicatePageError < Error
attr_accessor :dir
attr_accessor :existing_path
attr_accessor :attempted_path
def self.add_filter(pattern, &replacement)
@@filters[pattern] = replacement
end
def initialize(dir, existing, attempted, message = nil)
@dir = dir
@existing_path = existing
@attempted_path = attempted
super(message || "Cannot write #{@dir}/#{@attempted_path}, found #{@dir}/#{@existing_path}.")
def self.apply_filters(wiki_page, data)
@@filters.each do |pattern, replacement|
params = replacement.parameters.length == 0 ? nil : wiki_page
data.gsub!(pattern, replacement.call(*params))
end
data
end
end
end
+124 -53
View File
@@ -1,4 +1,5 @@
# ~*~ encoding: utf-8 ~*~
# encoding: UTF-8
require 'cgi'
require 'sinatra'
require 'sinatra/namespace'
@@ -14,12 +15,15 @@ require 'pathname'
require 'gollum'
require 'gollum/assets'
require 'gollum/views/helpers'
require 'gollum/views/helpers/locale_helpers'
require 'gollum/views/layout'
require 'gollum/views/editable'
require 'gollum/views/has_page'
require 'gollum/views/has_user_icons'
require 'gollum/views/has_math'
require 'gollum/views/pagination'
require 'gollum/views/rss.rb'
require 'gollum/views/template_cascade'
require File.expand_path '../helpers', __FILE__
@@ -40,7 +44,7 @@ Gollum::set_git_max_filesize(190 * 10**6)
# See the wiki.rb file for more details on wiki options
module Precious
# For use with the --base-path option.
class MapGollum
def initialize(base_path)
@@ -63,12 +67,14 @@ module Precious
@mg.call(env)
end
end
class App < Sinatra::Base
register Mustache::Sinatra
register Sinatra::Namespace
include Precious::Helpers
Encoding.default_external = "UTF-8"
dir = File.dirname(File.expand_path(__FILE__))
set :sprockets, ::Precious::Assets.sprockets(dir)
@@ -97,16 +103,18 @@ module Precious
end
before do
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
@allow_editing = settings.wiki_options[:allow_editing]
@allow_editing = settings.wiki_options.fetch(:allow_editing, true)
@critic_markup = settings.wiki_options[:critic_markup]
@redirects_enabled = settings.wiki_options.fetch(:redirects_enabled, true)
@per_page_uploads = settings.wiki_options[:per_page_uploads]
@show_local_time = settings.wiki_options.fetch(:show_local_time, false)
@wiki_title = settings.wiki_options.fetch(:title, 'Gollum Wiki')
forbid unless @allow_editing || request.request_method == 'GET'
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
if settings.wiki_options[:template_dir]
Precious::Views::Layout.extend Precious::Views::TemplateCascade
Precious::Views::Layout.template_priority_path = settings.wiki_options[:template_dir]
end
@base_url = url('/', false).chomp('/').force_encoding('utf-8')
@page_dir = settings.wiki_options[:page_file_dir].to_s
@@ -116,11 +124,12 @@ module Precious
@css = settings.wiki_options[:css]
@js = settings.wiki_options[:js]
@mathjax_config = settings.wiki_options[:mathjax_config]
@mathjax = settings.wiki_options[:mathjax]
@use_static_assets = settings.wiki_options.fetch(:static, settings.environment == :production || settings.environment == :staging)
@use_static_assets = settings.wiki_options.fetch(:static, settings.environment != :development)
@static_assets_path = settings.wiki_options.fetch(:static_assets_path, ::File.join(File.dirname(__FILE__), 'public/assets'))
@mathjax_path = ::File.join(File.dirname(__FILE__), 'public/gollum/javascript/MathJax')
Sprockets::Helpers.configure do |config|
config.environment = settings.sprockets
config.environment.context_class.class_variable_set(:@@base_url, @base_url)
@@ -131,6 +140,8 @@ module Precious
config.manifest = Sprockets::Manifest.new(settings.sprockets, @static_assets_path)
end
end
forbid unless @allow_editing || request.request_method == 'GET'
end
get '/' do
@@ -167,7 +178,9 @@ module Precious
content_type :json
if page = wiki_page(params[:path]).page
version = page.last_version
{:author => version.author.name, :date => version.authored_date}.to_json
authored_date = version.authored_date
authored_date = authored_date.utc.iso8601 if @show_local_time
{:author => version.author.name, :date => authored_date}.to_json
end
end
@@ -198,7 +211,7 @@ module Precious
end
get '/edit/*' do
forbid unless @allow_editing
forbid unless @allow_editing && @user_authed
wikip = wiki_page(params[:splat].first)
@name = wikip.fullname
@path = wikip.path
@@ -208,7 +221,6 @@ module Precious
if page = wikip.page
@page = page
@content = page.text_data
@mathjax = wiki.mathjax
@etag = page.sha
mustache :edit
else
@@ -219,9 +231,10 @@ module Precious
# AJAX calls only
post '/upload_file' do
wiki = wiki_new
halt 405 unless wiki.allow_uploads
forbid unless @user_authed
if params[:file]
fullname = params[:file][:filename]
@@ -230,12 +243,12 @@ module Precious
halt 500 unless tempfile.is_a? Tempfile
if wiki.per_page_uploads
dir = request.referer.sub(request.base_url, '')
dir = request.referer.match(/^https?:\/\/#{request.host_with_port}\/(.*)/)[1]
# remove base path if it is set
dir.sub!(/^#{wiki.base_path}/, '') if wiki.base_path
# remove base_url and gollum/* subpath if necessary
dir.sub!(/^\/gollum\/[-\w]+\//, '')
# remove file extension
# remove file extension
dir.sub!(/#{::File.extname(dir)}$/, '')
# revert escaped whitespaces
dir.gsub!(/%20/, ' ')
@@ -261,7 +274,7 @@ module Precious
options.merge! author
end
normalize = Gollum::Page.valid_extension?(fullname)
options[:normalize] = Gollum::Page.valid_extension?(fullname)
begin
wiki.write_file(reponame, contents, options)
@@ -278,6 +291,7 @@ module Precious
post '/rename/*' do
wikip = wiki_page(params[:splat].first)
halt 500 if wikip.nil?
forbid unless @user_authed
wiki = wikip.wiki
page = wikip.page
rename = params[:rename]
@@ -303,12 +317,13 @@ module Precious
redirect to("/#{page.escaped_url_path}")
return
end
committer.commit
# Renaming preserves format, so add the page's format to the renamed path to retrieve the renamed page
new_path = "#{rename}.#{Gollum::Page.format_to_ext(page.format)}"
# Add a redirect from the old page to the new
wiki.add_redirect(page.url_path, clean_url(new_path)) if @redirects_enabled
wiki.add_redirect(page.url_path, clean_url(new_path), commit) if @redirects_enabled
committer.commit
page = wiki_page(new_path).page
return if page.nil?
@@ -316,18 +331,18 @@ module Precious
end
post '/edit/*' do
etag = params[:etag]
etag = params[:etag]
path = "/#{clean_url(sanitize_empty_params(params[:path]))}"
page_name = CGI.unescape(params[:page])
wiki = wiki_new
page = wiki.page(::File.join(path, page_name))
page = wiki.page(::File.join(path, params[:page]))
forbid unless @user_authed
return if page.nil?
if etag != page.sha
# Signal edit collision and return the page's most recent version
halt 412, {etag: page.sha, text_data: page.text_data}.to_json
end
committer = Gollum::Committer.new(wiki, commit_message)
commit = { :committer => committer }
@@ -341,6 +356,7 @@ module Precious
post '/delete/*' do
forbid unless @allow_editing
forbid unless @user_authed
wiki = wiki_new
filepath = params[:splat].first
unless filepath.nil?
@@ -348,18 +364,16 @@ module Precious
commit[:message] = "Deleted #{filepath}"
wiki.delete_file(filepath, commit)
end
end
end
get '/create/*' do
forbid unless @allow_editing
if settings.wiki_options[:template_page] then
temppage = wiki_page('/_Template')
@template_page = (temppage.page != nil) ? temppage.page.raw_data : 'Template page option is set, but no /_Template page is present or committed.'
end
forbid unless @user_authed
wikip = wiki_page(params[:splat].first)
@name = wikip.name
@ext = wikip.ext
@path = wikip.path
@template_page = load_template(wikip, @path) if settings.wiki_options[:template_page]
@allow_uploads = wikip.wiki.allow_uploads
@upload_dest = find_upload_dest(wikip.fullpath)
@@ -380,6 +394,7 @@ module Precious
path = sanitize_empty_params(params[:path]) || ''
format = params[:format].intern
wiki = wiki_new
forbid unless @user_authed
path.gsub!(/^\//, '')
@@ -417,7 +432,7 @@ module Precious
post '/preview' do
wiki = wiki_new
@name = params[:page] ? strip_page_name(CGI.unescape(params[:page])) : 'Preview'
@name = params[:page] ? strip_page_name(params[:page]) : 'Preview'
@page = wiki.preview_page(@name, params[:content], params[:format])
['sidebar', 'header', 'footer'].each do |subpage|
@page.send("set_#{subpage}".to_sym, params[subpage]) if params[subpage]
@@ -433,23 +448,18 @@ module Precious
mustache :page
end
get %r{
/history/ # match any URL beginning with /history/
(.+?) # extract the full path (including any directories)
/
([0-9a-f]{40}) # match SHA
}x do |path, version|
wiki = wiki_new
show_history wiki_page(path, wiki.commit_for(version), wiki)
end
get '/history/*' do
wikip = wiki_page(params[:splat].first)
@name = wikip.fullname
@page = wikip.page
@page_num = [params[:page_num].to_i, 1].max
@max_count = settings.wiki_options.fetch(:pagination_count, 10)
unless @page.nil?
@wiki = @page.wiki
@versions = @page.versions(
per_page: @max_count,
page_num: @page_num,
follow: settings.wiki_options.fetch(:follow_renames, true)
)
mustache :history
else
redirect to("/")
end
show_history wiki_page(params[:splat].first)
end
get '/latest_changes' do
@@ -596,9 +606,61 @@ module Precious
private
def show_history(wikip)
@name = wikip.fullname
@page = wikip.page
@page_num = [params[:page_num].to_i, 1].max
@max_count = settings.wiki_options.fetch(:pagination_count, 10)
unless @page.nil?
@wiki = @page.wiki
@versions = @page.versions(
per_page: @max_count,
page_num: @page_num,
follow: settings.wiki_options.fetch(:follow_renames, true)
)
mustache :history
else
redirect to("/")
end
end
def show_page_or_file(fullpath)
wiki = wiki_new
if page = wiki.page(fullpath)
# check if folders exists
contents = wiki.tree_list
print contents
entry = contents.detect do |entry|
Gollum::File::path_match(fullpath, entry)
end
print entry
#if path
# @path = Pathname.new(path).cleanpath.to_s
# check_path = wiki.page_file_dir ? ::File.join(wiki.page_file_dir, @path, '/') : "#{@path}/"
# @results.select! {|result| result.path.start_with?(check_path) }
#end
#@results.sort_by! {|result| result.name.downcase}
#elsif fullpath[-1] == '/' ? Gollum::Page.valid_page_name?(fullpath) : false
if page = fullpath[-1] != '/' ? wiki.page("#{fullpath}/#{wiki.index_page}") : false
@page = page
@name = page.filename_stripped
@content = page.formatted_data
@upload_dest = find_upload_dest(Pathname.new(fullpath).cleanpath.to_s)
# Extensions and layout data
@editable = true
@toc_content = wiki.universal_toc ? @page.toc_data : nil
@h1_title = wiki.h1_title
@bar_side = wiki.bar_side
@allow_uploads = wiki.allow_uploads
@navbar = true
mustache :page
elsif page = wiki.page(fullpath)
@page = page
@name = page.filename_stripped
@content = page.formatted_data
@@ -607,7 +669,6 @@ module Precious
# Extensions and layout data
@editable = true
@toc_content = wiki.universal_toc ? @page.toc_data : nil
@mathjax = wiki.mathjax
@h1_title = wiki.h1_title
@bar_side = wiki.bar_side
@allow_uploads = wiki.allow_uploads
@@ -617,8 +678,13 @@ module Precious
show_file(file)
elsif @redirects_enabled && redirect_path = wiki.redirects[fullpath]
redirect to("#{encodeURIComponent(redirect_path)}?redirected_from=#{encodeURIComponent(fullpath)}")
elsif entry
# check if the folder fullpath exists
# if so, redirect to overview
#
redirect to("/gollum/overview/#{clean_url(encodeURIComponent(fullpath))}")
else
if @allow_editing
if @allow_editing && @user_authed
path = fullpath[-1] == '/' ? "#{fullpath}#{wiki.index_page}" : fullpath # Append default index page if no page name is supplied
redirect to("/gollum/create/#{clean_url(encodeURIComponent(path))}")
else
@@ -628,7 +694,7 @@ module Precious
end
end
end
def show_file(file)
return unless file
if file.on_disk?
@@ -639,6 +705,11 @@ module Precious
end
end
def load_template(wiki_page, path)
template_page = wiki_page(::File.join(path, '_Template')).page || wiki_page('/_Template').page
template_page ? Gollum::TemplateFilter.apply_filters(wiki_page, template_page.text_data) : nil
end
def update_wiki_page(wiki, page, content, commit, name = nil, format = nil)
return if !page ||
((!content || page.raw_data == content) && page.format == format)
@@ -648,9 +719,9 @@ module Precious
wiki.update_page(page, name, format, content.to_s, commit)
end
def wiki_page(path, version = nil)
def wiki_page(path, version = nil, wiki = nil)
pathname = (Pathname.new('/') + path).cleanpath
wiki = wiki_new
wiki = wiki_new if wiki.nil?
OpenStruct.new(:wiki => wiki, :page => wiki.page(pathname.to_s, version = version),
:name => pathname.basename.sub_ext('').to_s, :path => pathname.dirname.to_s, :ext => pathname.extname, :fullname => pathname.basename.to_s, :fullpath => pathname.to_s)
end
@@ -681,4 +752,4 @@ module Precious
end
end
end
end
@@ -0,0 +1 @@
{"files":{"app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js":{"logical_path":"app.js","mtime":"2021-11-15T20:08:30-08:00","size":136040,"digest":"f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5","integrity":"sha256-8FQB7jdPDH9I/CvAjjC09NtwWGH9WJXtcJmGg7ODv7U="},"editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js":{"logical_path":"editor.js","mtime":"2021-11-15T20:08:30-08:00","size":744886,"digest":"9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a","integrity":"sha256-mIHQx65mMpPw46fnJynux+lA+mExhcB2cJt20pL1cDo="},"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css":{"logical_path":"app.css","mtime":"2021-11-15T20:08:30-08:00","size":396731,"digest":"309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5","integrity":"sha256-MJvgMjlueDsTpH31jzibfI4RwrLUJkBWC4dPZ3wl9uU="},"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css":{"logical_path":"criticmarkup.css","mtime":"2021-08-22T15:24:51-07:00","size":646,"digest":"31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4","integrity":"sha256-Ma5dMoK7uOe3w8mRfp+2jjMVprSnXabOxI0huIRpBcQ="},"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css":{"logical_path":"print.css","mtime":"2021-08-22T15:24:51-07:00","size":75,"digest":"512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb","integrity":"sha256-USSYw2i+DT+xuhBd+oQomuSDgOyfy++Ui9TiOwsJW/s="}},"assets":{"app.js":"app-f05401ee374f0c7f48fc2bc08e30b4f4db705861fd5895ed70998683b383bfb5.js","editor.js":"editor-9881d0c7ae663293f0e3a7e72729eec7e940fa613185c076709b76d292f5703a.js","app.css":"app-309be032396e783b13a47df58f389b7c8e11c2b2d42640560b874f677c25f6e5.css","criticmarkup.css":"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css","print.css":"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css"}}
@@ -1 +0,0 @@
{"files":{"app-6e925e38a12a40c4fa9e0400cc874e0f4f97b66fdeb90a144dea527dbb544fbd.js":{"logical_path":"app.js","mtime":"2020-08-03T18:13:54+02:00","size":136032,"digest":"6e925e38a12a40c4fa9e0400cc874e0f4f97b66fdeb90a144dea527dbb544fbd","integrity":"sha256-bpJeOKEqQMT6ngQAzIdOD0+Xtm/euQoUTepSfbtUT70="},"editor-b2c10f22ef6ca1e120956a2d11ff4ca19ac44d7e7240f5cc43ec949184d8b708.js":{"logical_path":"editor.js","mtime":"2020-08-03T18:13:54+02:00","size":747273,"digest":"b2c10f22ef6ca1e120956a2d11ff4ca19ac44d7e7240f5cc43ec949184d8b708","integrity":"sha256-ssEPIu9soeEglWotEf9MoZrETX5yQPXMQ+yUkYTYtwg="},"app-b205e593a30f1cc0054e2e9ed9fc8af3658d8ef4a62b9708c20f204560deefb7.css":{"logical_path":"app.css","mtime":"2020-03-30T11:12:22+02:00","size":298111,"digest":"b205e593a30f1cc0054e2e9ed9fc8af3658d8ef4a62b9708c20f204560deefb7","integrity":"sha256-sgXlk6MPHMAFTi6e2fyK82WNjvSmK5cIwg8gRWDe77c="},"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css":{"logical_path":"criticmarkup.css","mtime":"2020-03-29T22:28:51+02:00","size":646,"digest":"31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4","integrity":"sha256-Ma5dMoK7uOe3w8mRfp+2jjMVprSnXabOxI0huIRpBcQ="},"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css":{"logical_path":"print.css","mtime":"2020-03-30T11:12:22+02:00","size":75,"digest":"512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb","integrity":"sha256-USSYw2i+DT+xuhBd+oQomuSDgOyfy++Ui9TiOwsJW/s="}},"assets":{"app.js":"app-6e925e38a12a40c4fa9e0400cc874e0f4f97b66fdeb90a144dea527dbb544fbd.js","editor.js":"editor-b2c10f22ef6ca1e120956a2d11ff4ca19ac44d7e7240f5cc43ec949184d8b708.js","app.css":"app-b205e593a30f1cc0054e2e9ed9fc8af3658d8ef4a62b9708c20f204560deefb7.css","criticmarkup.css":"criticmarkup-31ae5d3282bbb8e7b7c3c9917e9fb68e3315a6b4a75da6cec48d21b8846905c4.css","print.css":"print-512498c368be0d3fb1ba105dfa84289ae48380ec9fcbef948bd4e23b0b095bfb.css"}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
//= require jquery-1.7.2.min
//= require identicon
//= require date.min
//= require mousetrap.min
//= require clipboard.min
//= require gollum
@@ -7,3 +8,4 @@
//= require gollum.placeholder
//= require editor/sections
//= require jquery.resize
//= require polyfills
+123
View File
@@ -0,0 +1,123 @@
/**
* @overview datejs
* @version 1.0.0-rc3
* @author Gregory Wild-Smith <gregory@wild-smith.com>
* @copyright 2015 Gregory Wild-Smith
* @license MIT
* @homepage https://github.com/abritinthebay/datejs
*/
/*
2015 Gregory Wild-Smith
@license MIT
@homepage https://github.com/abritinthebay/datejs
*/
(function(){var h=Date,f=Date.CultureStrings?Date.CultureStrings.lang:null,d={},c={getFromKey:function(a,b){var e;e=Date.CultureStrings&&Date.CultureStrings[b]&&Date.CultureStrings[b][a]?Date.CultureStrings[b][a]:c.buildFromDefault(a);"/"===a.charAt(0)&&(e=c.buildFromRegex(a,b));return e},getFromObjectValues:function(a,b){var e,g={};for(e in a)a.hasOwnProperty(e)&&(g[e]=c.getFromKey(a[e],b));return g},getFromObjectKeys:function(a,b){var e,g={};for(e in a)a.hasOwnProperty(e)&&(g[c.getFromKey(e,b)]=
a[e]);return g},getFromArray:function(a,b){for(var e=[],g=0;g<a.length;g++)g in a&&(e[g]=c.getFromKey(a[g],b));return e},buildFromDefault:function(a){var b,e,g;switch(a){case "name":b="en-US";break;case "englishName":b="English (United States)";break;case "nativeName":b="English (United States)";break;case "twoDigitYearMax":b=2049;break;case "firstDayOfWeek":b=0;break;default:if(b=a,g=a.split("_"),e=g.length,1<e&&"/"!==a.charAt(0)&&(a=g[e-1].toLowerCase(),"initial"===a||"abbr"===a))b=g[0]}return b},
buildFromRegex:function(a,b){return Date.CultureStrings&&Date.CultureStrings[b]&&Date.CultureStrings[b][a]?new RegExp(Date.CultureStrings[b][a],"i"):new RegExp(a.replace(RegExp("/","g"),""),"i")}},a=function(a,b){var e=b?b:f;d[a]=a;return"object"===typeof a?a instanceof Array?c.getFromArray(a,e):c.getFromObjectKeys(a,e):c.getFromKey(a,e)},b=function(a){a=Date.Config.i18n+a+".js";var b=document.getElementsByTagName("head")[0]||document.documentElement,e=document.createElement("script");e.src=a;var g=
{done:function(){}};e.onload=e.onreadystatechange=function(){this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState||(g.done(),b.removeChild(e))};setTimeout(function(){b.insertBefore(e,b.firstChild)},0);return{done:function(a){g.done=function(){a&&setTimeout(a,0)}}}},e={buildFromMethodHash:function(a){for(var b in a)a.hasOwnProperty(b)&&(a[b]=e[a[b]]());return a},timeZoneDST:function(){return a({CHADT:"+1345",NZDT:"+1300",AEDT:"+1100",ACDT:"+1030",AZST:"+0500",IRDT:"+0430",EEST:"+0300",
CEST:"+0200",BST:"+0100",PMDT:"-0200",ADT:"-0300",NDT:"-0230",EDT:"-0400",CDT:"-0500",MDT:"-0600",PDT:"-0700",AKDT:"-0800",HADT:"-0900"})},timeZoneStandard:function(){return a({LINT:"+1400",TOT:"+1300",CHAST:"+1245",NZST:"+1200",NFT:"+1130",SBT:"+1100",AEST:"+1000",ACST:"+0930",JST:"+0900",CWST:"+0845",CT:"+0800",ICT:"+0700",MMT:"+0630",BST:"+0600",NPT:"+0545",IST:"+0530",PKT:"+0500",AFT:"+0430",MSK:"+0400",IRST:"+0330",FET:"+0300",EET:"+0200",CET:"+0100",GMT:"+0000",UTC:"+0000",CVT:"-0100",GST:"-0200",
BRT:"-0300",NST:"-0330",AST:"-0400",EST:"-0500",CST:"-0600",MST:"-0700",PST:"-0800",AKST:"-0900",MIT:"-0930",HST:"-1000",SST:"-1100",BIT:"-1200"})},timeZones:function(a){var b;a.timezones=[];for(b in a.abbreviatedTimeZoneStandard)a.abbreviatedTimeZoneStandard.hasOwnProperty(b)&&a.timezones.push({name:b,offset:a.abbreviatedTimeZoneStandard[b]});for(b in a.abbreviatedTimeZoneDST)a.abbreviatedTimeZoneDST.hasOwnProperty(b)&&a.timezones.push({name:b,offset:a.abbreviatedTimeZoneDST[b],dst:!0});return a.timezones},
days:function(){return a("Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "))},dayAbbr:function(){return a("Sun Mon Tue Wed Thu Fri Sat".split(" "))},dayShortNames:function(){return a("Su Mo Tu We Th Fr Sa".split(" "))},dayFirstLetters:function(){return a("S_Sun_Initial M_Mon_Initial T_Tues_Initial W_Wed_Initial T_Thu_Initial F_Fri_Initial S_Sat_Initial".split(" "))},months:function(){return a("January February March April May June July August September October November December".split(" "))},
monthAbbr:function(){return a("Jan_Abbr Feb_Abbr Mar_Abbr Apr_Abbr May_Abbr Jun_Abbr Jul_Abbr Aug_Abbr Sep_Abbr Oct_Abbr Nov_Abbr Dec_Abbr".split(" "))},formatPatterns:function(){return c.getFromObjectValues({shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},
Date.i18n.currentLanguage())},regex:function(){return c.getFromObjectValues({inTheMorning:"/( in the )(morn(ing)?)\\b/",thisMorning:"/(this )(morn(ing)?)\\b/",amThisMorning:"/(\b\\d(am)? )(this )(morn(ing)?)/",inTheEvening:"/( in the )(even(ing)?)\\b/",thisEvening:"/(this )(even(ing)?)\\b/",pmThisEvening:"/(\b\\d(pm)? )(this )(even(ing)?)/",jan:"/jan(uary)?/",feb:"/feb(ruary)?/",mar:"/mar(ch)?/",apr:"/apr(il)?/",may:"/may/",jun:"/jun(e)?/",jul:"/jul(y)?/",aug:"/aug(ust)?/",sep:"/sep(t(ember)?)?/",
oct:"/oct(ober)?/",nov:"/nov(ember)?/",dec:"/dec(ember)?/",sun:"/^su(n(day)?)?/",mon:"/^mo(n(day)?)?/",tue:"/^tu(e(s(day)?)?)?/",wed:"/^we(d(nesday)?)?/",thu:"/^th(u(r(s(day)?)?)?)?/",fri:"/fr(i(day)?)?/",sat:"/^sa(t(urday)?)?/",future:"/^next/",past:"/^last|past|prev(ious)?/",add:"/^(\\+|aft(er)?|from|hence)/",subtract:"/^(\\-|bef(ore)?|ago)/",yesterday:"/^yes(terday)?/",today:"/^t(od(ay)?)?/",tomorrow:"/^tom(orrow)?/",now:"/^n(ow)?/",millisecond:"/^ms|milli(second)?s?/",second:"/^sec(ond)?s?/",
minute:"/^mn|min(ute)?s?/",hour:"/^h(our)?s?/",week:"/^w(eek)?s?/",month:"/^m(onth)?s?/",day:"/^d(ay)?s?/",year:"/^y(ear)?s?/",shortMeridian:"/^(a|p)/",longMeridian:"/^(a\\.?m?\\.?|p\\.?m?\\.?)/",timezone:"/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\\s*(\\+|\\-)\\s*\\d\\d\\d\\d?)|gmt|utc)/",ordinalSuffix:"/^\\s*(st|nd|rd|th)/",timeContext:"/^\\s*(\\:|a(?!u|p)|p)/"},Date.i18n.currentLanguage())}},g=function(){var a=c.getFromObjectValues({name:"name",englishName:"englishName",nativeName:"nativeName",
amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:"firstDayOfWeek",twoDigitYearMax:"twoDigitYearMax",dateElementOrder:"mdy"},Date.i18n.currentLanguage()),b=e.buildFromMethodHash({dayNames:"days",abbreviatedDayNames:"dayAbbr",shortestDayNames:"dayShortNames",firstLetterDayNames:"dayFirstLetters",monthNames:"months",abbreviatedMonthNames:"monthAbbr",formatPatterns:"formatPatterns",regexPatterns:"regex",abbreviatedTimeZoneDST:"timeZoneDST",abbreviatedTimeZoneStandard:"timeZoneStandard"}),g;for(g in b)b.hasOwnProperty(g)&&
(a[g]=b[g]);e.timeZones(a);return a};h.i18n={__:function(m,b){return a(m,b)},currentLanguage:function(){return f||"en-US"},setLanguage:function(a,e,c){var d=!1;if(e||"en-US"===a||Date.CultureStrings&&Date.CultureStrings[a])f=a,Date.CultureStrings=Date.CultureStrings||{},Date.CultureStrings.lang=a,Date.CultureInfo=new g;else if(!Date.CultureStrings||!Date.CultureStrings[a])if("undefined"!==typeof exports&&this.exports!==exports)try{require("../i18n/"+a+".js"),f=a,Date.CultureStrings.lang=a,Date.CultureInfo=
new g}catch(p){throw Error("The DateJS IETF language tag '"+a+"' could not be loaded by Node. It likely does not exist.");}else if(Date.Config&&Date.Config.i18n)d=!0,b(a).done(function(){f=a;Date.CultureStrings=Date.CultureStrings||{};Date.CultureStrings.lang=a;Date.CultureInfo=new g;h.Parsing.Normalizer.buildReplaceData();h.Grammar&&h.Grammar.buildGrammarFormats();c&&setTimeout(c,0)});else return Date.console.error("The DateJS IETF language tag '"+a+"' is not available and has not been loaded."),
!1;h.Parsing.Normalizer.buildReplaceData();h.Grammar&&h.Grammar.buildGrammarFormats();!d&&c&&setTimeout(c,0)},getLoggedKeys:function(){return d},updateCultureInfo:function(){Date.CultureInfo=new g}};h.i18n.updateCultureInfo()})();
(function(){var h=Date,f=h.prototype,d=function(a,b){b||(b=2);return("000"+a).slice(-1*b)};h.console="undefined"!==typeof window&&"undefined"!==typeof window.console&&"undefined"!==typeof window.console.log?console:{log:function(){},error:function(){}};h.Config=h.Config||{};h.initOverloads=function(){h.now?h._now||(h._now=h.now):h._now=function(){return(new Date).getTime()};h.now=function(a){return a?h.present():h._now()};f.toISOString||(f.toISOString=function(){return this.getUTCFullYear()+"-"+d(this.getUTCMonth()+
1)+"-"+d(this.getUTCDate())+"T"+d(this.getUTCHours())+":"+d(this.getUTCMinutes())+":"+d(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1E3).toFixed(3)).slice(2,5)+"Z"});void 0===f._toString&&(f._toString=f.toString)};h.initOverloads();h.today=function(){return(new Date).clearTime()};h.present=function(){return new Date};h.compare=function(a,b){if(isNaN(a)||isNaN(b))throw Error(a+" - "+b);if(a instanceof Date&&b instanceof Date)return a<b?-1:a>b?1:0;throw new TypeError(a+" - "+b);};h.equals=
function(a,b){return 0===a.compareTo(b)};h.getDayName=function(a){return Date.CultureInfo.dayNames[a]};h.getDayNumberFromName=function(a){var b=Date.CultureInfo.dayNames,e=Date.CultureInfo.abbreviatedDayNames,g=Date.CultureInfo.shortestDayNames;a=a.toLowerCase();for(var m=0;m<b.length;m++)if(b[m].toLowerCase()===a||e[m].toLowerCase()===a||g[m].toLowerCase()===a)return m;return-1};h.getMonthNumberFromName=function(a){var b=Date.CultureInfo.monthNames,e=Date.CultureInfo.abbreviatedMonthNames;a=a.toLowerCase();
for(var g=0;g<b.length;g++)if(b[g].toLowerCase()===a||e[g].toLowerCase()===a)return g;return-1};h.getMonthName=function(a){return Date.CultureInfo.monthNames[a]};h.isLeapYear=function(a){return 0===a%4&&0!==a%100||0===a%400};h.getDaysInMonth=function(a,b){!b&&h.validateMonth(a)&&(b=a,a=Date.today().getFullYear());return[31,h.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]};f.getDaysInMonth=function(){return h.getDaysInMonth(this.getFullYear(),this.getMonth())};h.getTimezoneAbbreviation=function(a,
b){var e,g=b?Date.CultureInfo.abbreviatedTimeZoneDST:Date.CultureInfo.abbreviatedTimeZoneStandard;for(e in g)if(g.hasOwnProperty(e)&&g[e]===a)return e;return null};h.getTimezoneOffset=function(a,b){var e,g=[],m=Date.CultureInfo.timezones;a||(a=(new Date).getTimezone());for(e=0;e<m.length;e++)m[e].name===a.toUpperCase()&&g.push(e);if(!m[g[0]])return null;if(1!==g.length&&b)for(e=0;e<g.length;e++){if(m[g[e]].dst)return m[g[e]].offset}else return m[g[0]].offset};h.getQuarter=function(a){a=a||new Date;
return[1,2,3,4][Math.floor(a.getMonth()/3)]};h.getDaysLeftInQuarter=function(a){a=a||new Date;var b=new Date(a);b.setMonth(b.getMonth()+3-b.getMonth()%3,0);return Math.floor((b-a)/864E5)};var c=function(a,b,e,g){if("undefined"===typeof a)return!1;if("number"!==typeof a)throw new TypeError(a+" is not a Number.");return a<b||a>e?!1:!0};h.validateMillisecond=function(a){return c(a,0,999,"millisecond")};h.validateSecond=function(a){return c(a,0,59,"second")};h.validateMinute=function(a){return c(a,0,
59,"minute")};h.validateHour=function(a){return c(a,0,23,"hour")};h.validateDay=function(a,b,e){return void 0===b||null===b||void 0===e||null===e?!1:c(a,1,h.getDaysInMonth(b,e),"day")};h.validateWeek=function(a){return c(a,0,53,"week")};h.validateMonth=function(a){return c(a,0,11,"month")};h.validateYear=function(a){return c(a,-271822,275760,"year")};h.validateTimezone=function(a){return 1==={ACDT:1,ACST:1,ACT:1,ADT:1,AEDT:1,AEST:1,AFT:1,AKDT:1,AKST:1,AMST:1,AMT:1,ART:1,AST:1,AWDT:1,AWST:1,AZOST:1,
AZT:1,BDT:1,BIOT:1,BIT:1,BOT:1,BRT:1,BST:1,BTT:1,CAT:1,CCT:1,CDT:1,CEDT:1,CEST:1,CET:1,CHADT:1,CHAST:1,CHOT:1,ChST:1,CHUT:1,CIST:1,CIT:1,CKT:1,CLST:1,CLT:1,COST:1,COT:1,CST:1,CT:1,CVT:1,CWST:1,CXT:1,DAVT:1,DDUT:1,DFT:1,EASST:1,EAST:1,EAT:1,ECT:1,EDT:1,EEDT:1,EEST:1,EET:1,EGST:1,EGT:1,EIT:1,EST:1,FET:1,FJT:1,FKST:1,FKT:1,FNT:1,GALT:1,GAMT:1,GET:1,GFT:1,GILT:1,GIT:1,GMT:1,GST:1,GYT:1,HADT:1,HAEC:1,HAST:1,HKT:1,HMT:1,HOVT:1,HST:1,ICT:1,IDT:1,IOT:1,IRDT:1,IRKT:1,IRST:1,IST:1,JST:1,KGT:1,KOST:1,KRAT:1,
KST:1,LHST:1,LINT:1,MAGT:1,MART:1,MAWT:1,MDT:1,MET:1,MEST:1,MHT:1,MIST:1,MIT:1,MMT:1,MSK:1,MST:1,MUT:1,MVT:1,MYT:1,NCT:1,NDT:1,NFT:1,NPT:1,NST:1,NT:1,NUT:1,NZDT:1,NZST:1,OMST:1,ORAT:1,PDT:1,PET:1,PETT:1,PGT:1,PHOT:1,PHT:1,PKT:1,PMDT:1,PMST:1,PONT:1,PST:1,PYST:1,PYT:1,RET:1,ROTT:1,SAKT:1,SAMT:1,SAST:1,SBT:1,SCT:1,SGT:1,SLST:1,SRT:1,SST:1,SYOT:1,TAHT:1,THA:1,TFT:1,TJT:1,TKT:1,TLT:1,TMT:1,TOT:1,TVT:1,UCT:1,ULAT:1,UTC:1,UYST:1,UYT:1,UZT:1,VET:1,VLAT:1,VOLT:1,VOST:1,VUT:1,WAKT:1,WAST:1,WAT:1,WEDT:1,WEST:1,
WET:1,WST:1,YAKT:1,YEKT:1,Z:1}[a]};h.validateTimezoneOffset=function(a){return-841<a&&721>a}})();
(function(){var h=Date,f=h.prototype,d=function(a,b){b||(b=2);return("000"+a).slice(-1*b)},c=function(a){var b={},e=this,g,c;c=function(b,g,c){if("day"===b){b=void 0!==a.month?a.month:e.getMonth();var d=void 0!==a.year?a.year:e.getFullYear();return h[g](c,d,b)}return h[g](c)};for(g in a)if(Object.prototype.hasOwnProperty.call(a,g)){var d="validate"+g.charAt(0).toUpperCase()+g.slice(1);h[d]&&null!==a[g]&&c(g,d,a[g])&&(b[g]=a[g])}return b};f.clearTime=function(){this.setHours(0);this.setMinutes(0);
this.setSeconds(0);this.setMilliseconds(0);return this};f.setTimeToNow=function(){var a=new Date;this.setHours(a.getHours());this.setMinutes(a.getMinutes());this.setSeconds(a.getSeconds());this.setMilliseconds(a.getMilliseconds());return this};f.clone=function(){return new Date(this.getTime())};f.compareTo=function(a){return Date.compare(this,a)};f.equals=function(a){return Date.equals(this,void 0!==a?a:new Date)};f.between=function(a,b){return this.getTime()>=a.getTime()&&this.getTime()<=b.getTime()};
f.isAfter=function(a){return 1===this.compareTo(a||new Date)};f.isBefore=function(a){return-1===this.compareTo(a||new Date)};f.isToday=f.isSameDay=function(a){return this.clone().clearTime().equals((a||new Date).clone().clearTime())};f.addMilliseconds=function(a){if(!a)return this;this.setTime(this.getTime()+1*a);return this};f.addSeconds=function(a){return a?this.addMilliseconds(1E3*a):this};f.addMinutes=function(a){return a?this.addMilliseconds(6E4*a):this};f.addHours=function(a){return a?this.addMilliseconds(36E5*
a):this};f.addDays=function(a){if(!a)return this;this.setDate(this.getDate()+1*a);return this};f.addWeekdays=function(a){if(!a)return this;var b=this.getDay(),e=Math.ceil(Math.abs(a)/7);(0===b||6===b)&&0<a&&(this.next().monday(),this.addDays(-1),b=this.getDay());if(0>a){for(;0>a;)this.addDays(-1),b=this.getDay(),0!==b&&6!==b&&a++;return this}if(5<a||6-b<=a)a+=2*e;return this.addDays(a)};f.addWeeks=function(a){return a?this.addDays(7*a):this};f.addMonths=function(a){if(!a)return this;var b=this.getDate();
this.setDate(1);this.setMonth(this.getMonth()+1*a);this.setDate(Math.min(b,h.getDaysInMonth(this.getFullYear(),this.getMonth())));return this};f.addQuarters=function(a){return a?this.addMonths(3*a):this};f.addYears=function(a){return a?this.addMonths(12*a):this};f.add=function(a){if("number"===typeof a)return this._orient=a,this;a.day&&0!==a.day-this.getDate()&&this.setDate(a.day);a.milliseconds&&this.addMilliseconds(a.milliseconds);a.seconds&&this.addSeconds(a.seconds);a.minutes&&this.addMinutes(a.minutes);
a.hours&&this.addHours(a.hours);a.weeks&&this.addWeeks(a.weeks);a.months&&this.addMonths(a.months);a.years&&this.addYears(a.years);a.days&&this.addDays(a.days);return this};f.getWeek=function(a){var b=new Date(this.valueOf());a?(b.addMinutes(b.getTimezoneOffset()),a=b.clone()):a=this;a=(a.getDay()+6)%7;b.setDate(b.getDate()-a+3);a=b.valueOf();b.setMonth(0,1);4!==b.getDay()&&b.setMonth(0,1+(4-b.getDay()+7)%7);return 1+Math.ceil((a-b)/6048E5)};f.getISOWeek=function(){return d(this.getWeek(!0))};f.setWeek=
function(a){return 0===a-this.getWeek()?1!==this.getDay()?this.moveToDayOfWeek(1,1<this.getDay()?-1:1):this:this.moveToDayOfWeek(1,1<this.getDay()?-1:1).addWeeks(a-this.getWeek())};f.setQuarter=function(a){a=Math.abs(3*(a-1)+1);return this.setMonth(a,1)};f.getQuarter=function(){return Date.getQuarter(this)};f.getDaysLeftInQuarter=function(){return Date.getDaysLeftInQuarter(this)};f.moveToNthOccurrence=function(a,b){if("Weekday"===a){if(0<b)this.moveToFirstDayOfMonth(),this.is().weekday()&&--b;else if(0>
b)this.moveToLastDayOfMonth(),this.is().weekday()&&(b+=1);else return this;return this.addWeekdays(b)}var e=0;if(0<b)e=b-1;else if(-1===b)return this.moveToLastDayOfMonth(),this.getDay()!==a&&this.moveToDayOfWeek(a,-1),this;return this.moveToFirstDayOfMonth().addDays(-1).moveToDayOfWeek(a,1).addWeeks(e)};var a=function(a,b,e){return function(g,c){var d=(g-this[a]()+e*(c||1))%e;return this[b](0===d?d+e*(c||1):d)}};f.moveToDayOfWeek=a("getDay","addDays",7);f.moveToMonth=a("getMonth","addMonths",12);
f.getOrdinate=function(){var a=this.getDate();return b(a)};f.getOrdinalNumber=function(){return Math.ceil((this.clone().clearTime()-new Date(this.getFullYear(),0,1))/864E5)+1};f.getTimezone=function(){return h.getTimezoneAbbreviation(this.getUTCOffset(),this.isDaylightSavingTime())};f.setTimezoneOffset=function(a){var b=this.getTimezoneOffset();return(a=-6*Number(a)/10)||0===a?this.addMinutes(a-b):this};f.setTimezone=function(a){return this.setTimezoneOffset(h.getTimezoneOffset(a))};f.hasDaylightSavingTime=
function(){return Date.today().set({month:0,day:1}).getTimezoneOffset()!==Date.today().set({month:6,day:1}).getTimezoneOffset()};f.isDaylightSavingTime=function(){return Date.today().set({month:0,day:1}).getTimezoneOffset()!==this.getTimezoneOffset()};f.getUTCOffset=function(a){a=-10*(a||this.getTimezoneOffset())/6;if(0>a)return a=(a-1E4).toString(),a.charAt(0)+a.substr(2);a=(a+1E4).toString();return"+"+a.substr(1)};f.getElapsed=function(a){return(a||new Date)-this};f.set=function(a){a=c.call(this,
a);for(var b in a)if(Object.prototype.hasOwnProperty.call(a,b)){var e=b.charAt(0).toUpperCase()+b.slice(1),g,d;"week"!==b&&"month"!==b&&"timezone"!==b&&"timezoneOffset"!==b&&(e+="s");g="add"+e;d="get"+e;"month"===b?g+="s":"year"===b&&(d="getFullYear");if("day"!==b&&"timezone"!==b&&"timezoneOffset"!==b&&"week"!==b&&"hour"!==b)this[g](a[b]-this[d]());else if("timezone"===b||"timezoneOffset"===b||"week"===b||"hour"===b)this["set"+e](a[b])}a.day&&this.addDays(a.day-this.getDate());return this};f.moveToFirstDayOfMonth=
function(){return this.set({day:1})};f.moveToLastDayOfMonth=function(){return this.set({day:h.getDaysInMonth(this.getFullYear(),this.getMonth())})};var b=function(a){switch(1*a){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th"}},e=function(a){var b=Date.CultureInfo.formatPatterns;switch(a){case "d":return this.toString(b.shortDate);case "D":return this.toString(b.longDate);case "F":return this.toString(b.fullDateTime);case "m":return this.toString(b.monthDay);
case "r":case "R":return a=this.clone().addMinutes(this.getTimezoneOffset()),a.toString(b.rfc1123)+" GMT";case "s":return this.toString(b.sortableDateTime);case "t":return this.toString(b.shortTime);case "T":return this.toString(b.longTime);case "u":return a=this.clone().addMinutes(this.getTimezoneOffset()),a.toString(b.universalSortableDateTime);case "y":return this.toString(b.yearMonth);default:return!1}},g=function(a){return function(e){if("\\"===e.charAt(0))return e.replace("\\","");switch(e){case "hh":return d(13>
a.getHours()?0===a.getHours()?12:a.getHours():a.getHours()-12);case "h":return 13>a.getHours()?0===a.getHours()?12:a.getHours():a.getHours()-12;case "HH":return d(a.getHours());case "H":return a.getHours();case "mm":return d(a.getMinutes());case "m":return a.getMinutes();case "ss":return d(a.getSeconds());case "s":return a.getSeconds();case "yyyy":return d(a.getFullYear(),4);case "yy":return d(a.getFullYear());case "y":return a.getFullYear();case "E":case "dddd":return Date.CultureInfo.dayNames[a.getDay()];
case "ddd":return Date.CultureInfo.abbreviatedDayNames[a.getDay()];case "dd":return d(a.getDate());case "d":return a.getDate();case "MMMM":return Date.CultureInfo.monthNames[a.getMonth()];case "MMM":return Date.CultureInfo.abbreviatedMonthNames[a.getMonth()];case "MM":return d(a.getMonth()+1);case "M":return a.getMonth()+1;case "t":return 12>a.getHours()?Date.CultureInfo.amDesignator.substring(0,1):Date.CultureInfo.pmDesignator.substring(0,1);case "tt":return 12>a.getHours()?Date.CultureInfo.amDesignator:
Date.CultureInfo.pmDesignator;case "S":return b(a.getDate());case "W":return a.getWeek();case "WW":return a.getISOWeek();case "Q":return"Q"+a.getQuarter();case "q":return String(a.getQuarter());case "z":return a.getTimezone();case "Z":case "X":return Date.getTimezoneOffset(a.getTimezone());case "ZZ":return-60*a.getTimezoneOffset();case "u":return a.getDay();case "L":return h.isLeapYear(a.getFullYear())?1:0;case "B":return"@"+(a.getUTCSeconds()+60*a.getUTCMinutes()+3600*(a.getUTCHours()+1))/86.4;default:return e}}};
f.toString=function(a,b){if(!b&&a&&1===a.length&&(output=e.call(this,a)))return output;var c=g(this);return a?a.replace(/((\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S|q|Q|WW?W?W?)(?![^\[]*\]))/g,c).replace(/\[|\]/g,""):this._toString()}})();
(function(){var h=Date,f=h.prototype,d=Number.prototype;f._orient=1;f._nth=null;f._is=!1;f._same=!1;f._isSecond=!1;d._dateElement="days";f.next=function(){this._move=!0;this._orient=1;return this};h.next=function(){return h.today().next()};f.last=f.prev=f.previous=function(){this._move=!0;this._orient=-1;return this};h.last=h.prev=h.previous=function(){return h.today().last()};f.is=function(){this._is=!0;return this};f.same=function(){this._same=!0;this._isSecond=!1;return this};f.today=function(){return this.same().day()};
f.weekday=function(){return this._nth?m("Weekday").call(this):this._move?this.addWeekdays(this._orient):this._is?(this._is=!1,!this.is().sat()&&!this.is().sun()):!1};f.weekend=function(){return this._is?(this._is=!1,this.is().sat()||this.is().sun()):!1};f.at=function(a){return"string"===typeof a?h.parse(this.toString("d")+" "+a):this.set(a)};d.fromNow=d.after=function(a){var b={};b[this._dateElement]=this;return(a?a.clone():new Date).add(b)};d.ago=d.before=function(a){var b={};b["s"!==this._dateElement[this._dateElement.length-
1]?this._dateElement+"s":this._dateElement]=-1*this;return(a?a.clone():new Date).add(b)};var c="sunday monday tuesday wednesday thursday friday saturday".split(/\s/),a="january february march april may june july august september october november december".split(/\s/),b="Millisecond Second Minute Hour Day Week Month Year Quarter Weekday".split(/\s/),e="Milliseconds Seconds Minutes Hours Date Week Month FullYear Quarter".split(/\s/),g="final first second third fourth fifth".split(/\s/);f.toObject=function(){for(var a=
{},g=0;g<b.length;g++)this["get"+e[g]]&&(a[b[g].toLowerCase()]=this["get"+e[g]]());return a};h.fromObject=function(a){a.week=null;return Date.today().set(a)};var m=function(a){return function(){if(this._is)return this._is=!1,this.getDay()===a;this._move&&(this._move=null);if(null!==this._nth){this._isSecond&&this.addSeconds(-1*this._orient);this._isSecond=!1;var b=this._nth;this._nth=null;var e=this.clone().moveToLastDayOfMonth();this.moveToNthOccurrence(a,b);if(this>e)throw new RangeError(h.getDayName(a)+
" does not occur "+b+" times in the month of "+h.getMonthName(e.getMonth())+" "+e.getFullYear()+".");return this}return this.moveToDayOfWeek(a,this._orient)}},k=function(a,b,e){for(var g=0;g<a.length;g++)h[a[g].toUpperCase()]=h[a[g].toUpperCase().substring(0,3)]=g,h[a[g]]=h[a[g].substring(0,3)]=b(g),f[a[g]]=f[a[g].substring(0,3)]=e(g)};k(c,function(a){return function(){var b=h.today(),e=a-b.getDay();0===a&&1===Date.CultureInfo.firstDayOfWeek&&0!==b.getDay()&&(e+=7);return b.addDays(e)}},m);k(a,function(a){return function(){return h.today().set({month:a,
day:1})}},function(a){return function(){return this._is?(this._is=!1,this.getMonth()===a):this.moveToMonth(a,this._orient)}});for(var a=function(a){return function(e){if(this._isSecond)return this._isSecond=!1,this;if(this._same){this._same=this._is=!1;var g=this.toObject();e=(e||new Date).toObject();for(var c="",d=a.toLowerCase(),d="s"===d[d.length-1]?d.substring(0,d.length-1):d,f=b.length-1;-1<f;f--){c=b[f].toLowerCase();if(g[c]!==e[c])return!1;if(d===c)break}return!0}"s"!==a.substring(a.length-
1)&&(a+="s");this._move&&(this._move=null);return this["add"+a](this._orient)}},k=function(a){return function(){this._dateElement=a;return this}},n=0;n<b.length;n++)c=b[n].toLowerCase(),"weekday"!==c&&(f[c]=f[c+"s"]=a(b[n]),d[c]=d[c+"s"]=k(c+"s"));f._ss=a("Second");d=function(a){return function(b){if(this._same)return this._ss(b);if(b||0===b)return this.moveToNthOccurrence(b,a);this._nth=a;return 2!==a||void 0!==b&&null!==b?this:(this._isSecond=!0,this.addSeconds(this._orient))}};for(c=0;c<g.length;c++)f[g[c]]=
0===c?d(-1):d(c)})();
(function(){Date.Parsing={Exception:function(a){this.message="Parse error at '"+a.substring(0,10)+" ...'"}};var h=Date.Parsing,f=[0,31,59,90,120,151,181,212,243,273,304,334],d=[0,31,60,91,121,152,182,213,244,274,305,335];h.isLeapYear=function(a){return 0===a%4&&0!==a%100||0===a%400};var c={multiReplace:function(a,b){for(var e in b)if(Object.prototype.hasOwnProperty.call(b,e)){var g;"function"!==typeof b[e]&&(g=b[e]instanceof RegExp?b[e]:new RegExp(b[e],"g"));a=a.replace(g,e)}return a},getDayOfYearFromWeek:function(a){var b;
a.weekDay=a.weekDay||0===a.weekDay?a.weekDay:1;b=new Date(a.year,0,4);b=(0===b.getDay()?7:b.getDay())+3;a.dayOfYear=7*a.week+(0===a.weekDay?7:a.weekDay)-b;return a},getDayOfYear:function(a,b){a.dayOfYear||(a=c.getDayOfYearFromWeek(a));for(var e=0;e<=b.length;e++)if(a.dayOfYear<b[e]||e===b.length){a.day=a.day?a.day:a.dayOfYear-b[e-1];break}else a.month=e;return a},adjustForTimeZone:function(a,b){var e;"Z"===a.zone.toUpperCase()||0===a.zone_hours&&0===a.zone_minutes?e=-b.getTimezoneOffset():(e=60*a.zone_hours+
(a.zone_minutes||0),"+"===a.zone_sign&&(e*=-1),e-=b.getTimezoneOffset());b.setMinutes(b.getMinutes()+e);return b},setDefaults:function(a){a.year=a.year||Date.today().getFullYear();a.hours=a.hours||0;a.minutes=a.minutes||0;a.seconds=a.seconds||0;a.milliseconds=a.milliseconds||0;if(a.month||!a.week&&!a.dayOfYear)a.month=a.month||0,a.day=a.day||1;return a},dataNum:function(a,b,e,g){var c=1*a;return b?g?a?1*b(a):a:a?b(c):a:e?a&&"undefined"!==typeof a?c:a:a?c:a},timeDataProcess:function(a){var b={},e;
for(e in a.data)a.data.hasOwnProperty(e)&&(b[e]=a.ignore[e]?a.data[e]:c.dataNum(a.data[e],a.mods[e],a.explict[e],a.postProcess[e]));a.data.secmins&&(a.data.secmins=60*a.data.secmins.replace(",","."),b.minutes?b.seconds||(b.seconds=a.data.secmins):b.minutes=a.data.secmins,delete a.secmins);return b},buildTimeObjectFromData:function(a){return c.timeDataProcess({data:{year:a[1],month:a[5],day:a[7],week:a[8],dayOfYear:a[10],hours:a[15],zone_hours:a[23],zone_minutes:a[24],zone:a[21],zone_sign:a[22],weekDay:a[9],
minutes:a[16],seconds:a[19],milliseconds:a[20],secmins:a[18]},mods:{month:function(a){return a-1},weekDay:function(a){a=Math.abs(a);return 7===a?0:a},minutes:function(a){return a.replace(":","")},seconds:function(a){return Math.floor(1*a.replace(":","").replace(",","."))},milliseconds:function(a){return 1E3*a.replace(",",".")}},postProcess:{minutes:!0,seconds:!0,milliseconds:!0},explict:{zone_hours:!0,zone_minutes:!0},ignore:{zone:!0,zone_sign:!0,secmins:!0}})},addToHash:function(a,b,e){for(var g=
b.length,c=0;c<g;c++)a[b[c]]=e[c];return a},combineRegex:function(a,b){return new RegExp("(("+a.source+")\\s("+b.source+"))")},getDateNthString:function(a,b,e){if(a)return Date.today().addDays(e).toString("d");if(b)return Date.today().last()[e]().toString("d")},buildRegexData:function(a){for(var b=[],e=a.length,g=0;g<e;g++)"[object Array]"===Object.prototype.toString.call(a[g])?b.push(this.combineRegex(a[g][0],a[g][1])):b.push(a[g]);return b}};h.processTimeObject=function(a){var b;c.setDefaults(a);
b=h.isLeapYear(a.year)?d:f;a.month||!a.week&&!a.dayOfYear?a.dayOfYear=b[a.month]+a.day:c.getDayOfYear(a,b);b=new Date(a.year,a.month,a.day,a.hours,a.minutes,a.seconds,a.milliseconds);a.zone&&c.adjustForTimeZone(a,b);return b};h.ISO={regex:/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-4])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?\s?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/,
parse:function(a){a=a.match(this.regex);if(!a||!a.length)return null;a=c.buildTimeObjectFromData(a);return a.year&&(a.year||a.month||a.day||a.week||a.dayOfYear)?h.processTimeObject(a):null}};h.Numeric={isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},regex:/\b([0-1]?[0-9])([0-3]?[0-9])([0-2]?[0-9]?[0-9][0-9])\b/i,parse:function(a){var b,e={},g=Date.CultureInfo.dateElementOrder.split("");if(!this.isNumeric(a)||"+"===a[0]&&"-"===a[0])return null;if(5>a.length&&0>a.indexOf(".")&&0>a.indexOf("/"))return e.year=
a,h.processTimeObject(e);a=a.match(this.regex);if(!a||!a.length)return null;for(b=0;b<g.length;b++)switch(g[b]){case "d":e.day=a[b+1];break;case "m":e.month=a[b+1]-1;break;case "y":e.year=a[b+1]}return h.processTimeObject(e)}};h.Normalizer={regexData:function(){var a=Date.CultureInfo.regexPatterns;return c.buildRegexData([a.tomorrow,a.yesterday,[a.past,a.mon],[a.past,a.tue],[a.past,a.wed],[a.past,a.thu],[a.past,a.fri],[a.past,a.sat],[a.past,a.sun]])},basicReplaceHash:function(){var a=Date.CultureInfo.regexPatterns;
return{January:a.jan.source,February:a.feb,March:a.mar,April:a.apr,May:a.may,June:a.jun,July:a.jul,August:a.aug,September:a.sep,October:a.oct,November:a.nov,December:a.dec,"":/\bat\b/gi," ":/\s{2,}/,am:a.inTheMorning,"9am":a.thisMorning,pm:a.inTheEvening,"7pm":a.thisEvening}},keys:function(){return[c.getDateNthString(!0,!1,1),c.getDateNthString(!0,!1,-1),c.getDateNthString(!1,!0,"monday"),c.getDateNthString(!1,!0,"tuesday"),c.getDateNthString(!1,!0,"wednesday"),c.getDateNthString(!1,!0,"thursday"),
c.getDateNthString(!1,!0,"friday"),c.getDateNthString(!1,!0,"saturday"),c.getDateNthString(!1,!0,"sunday")]},buildRegexFunctions:function(){var a=Date.CultureInfo.regexPatterns,b=Date.i18n.__,b=new RegExp("(\\b\\d\\d?("+b("AM")+"|"+b("PM")+")? )("+a.tomorrow.source.slice(1)+")","i");this.replaceFuncs=[[new RegExp(a.today.source+"(?!\\s*([+-]))\\b"),function(a){return 1<a.length?Date.today().toString("d"):a}],[b,function(a,b){return Date.today().addDays(1).toString("d")+" "+b}],[a.amThisMorning,function(a,
b){return b}],[a.pmThisEvening,function(a,b){return b}]]},buildReplaceData:function(){this.buildRegexFunctions();this.replaceHash=c.addToHash(this.basicReplaceHash(),this.keys(),this.regexData())},stringReplaceFuncs:function(a){for(var b=0;b<this.replaceFuncs.length;b++)a=a.replace(this.replaceFuncs[b][0],this.replaceFuncs[b][1]);return a},parse:function(a){a=this.stringReplaceFuncs(a);a=c.multiReplace(a,this.replaceHash);try{var b=a.split(/([\s\-\.\,\/\x27]+)/);3===b.length&&h.Numeric.isNumeric(b[0])&&
h.Numeric.isNumeric(b[2])&&4<=b[2].length&&"d"===Date.CultureInfo.dateElementOrder[0]&&(a="1/"+b[0]+"/"+b[2])}catch(e){}return a}};h.Normalizer.buildReplaceData()})();
(function(){for(var h=Date.Parsing,f=h.Operators={rtoken:function(a){return function(e){var g=e.match(a);if(g)return[g[0],e.substring(g[0].length)];throw new h.Exception(e);}},token:function(){return function(a){return f.rtoken(new RegExp("^\\s*"+a+"\\s*"))(a)}},stoken:function(a){return f.rtoken(new RegExp("^"+a))},until:function(a){return function(e){for(var g=[],c=null;e.length;){try{c=a.call(this,e)}catch(d){g.push(c[0]);e=c[1];continue}break}return[g,e]}},many:function(a){return function(e){for(var g=
[],c=null;e.length;){try{c=a.call(this,e)}catch(d){break}g.push(c[0]);e=c[1]}return[g,e]}},optional:function(a){return function(e){var g=null;try{g=a.call(this,e)}catch(c){return[null,e]}return[g[0],g[1]]}},not:function(a){return function(e){try{a.call(this,e)}catch(g){return[null,e]}throw new h.Exception(e);}},ignore:function(a){return a?function(e){var g=null,g=a.call(this,e);return[null,g[1]]}:null},product:function(){for(var a=arguments[0],e=Array.prototype.slice.call(arguments,1),g=[],c=0;c<
a.length;c++)g.push(f.each(a[c],e));return g},cache:function(a){var e={},g=0,c=[],d=Date.Config.CACHE_MAX||1E5,f=null;return function(l){if(g===d)for(var p=0;10>p;p++){var q=c.shift();q&&(delete e[q],g--)}try{f=e[l]=e[l]||a.call(this,l)}catch(s){f=e[l]=s}g++;c.push(l);if(f instanceof h.Exception)throw f;return f}},any:function(){var a=arguments;return function(e){for(var g=null,c=0;c<a.length;c++)if(null!=a[c]){try{g=a[c].call(this,e)}catch(d){g=null}if(g)return g}throw new h.Exception(e);}},each:function(){var a=
arguments;return function(e){for(var c=[],d=null,f=0;f<a.length;f++)if(null!=a[f]){try{d=a[f].call(this,e)}catch(n){throw new h.Exception(e);}c.push(d[0]);e=d[1]}return[c,e]}},all:function(){var a=a;return a.each(a.optional(arguments))},sequence:function(a,e,c){e=e||f.rtoken(/^\s*/);c=c||null;return 1===a.length?a[0]:function(d){for(var f=null,n=null,l=[],p=0;p<a.length;p++){try{f=a[p].call(this,d)}catch(q){break}l.push(f[0]);try{n=e.call(this,f[1])}catch(s){n=null;break}d=n[1]}if(!f)throw new h.Exception(d);
if(n)throw new h.Exception(n[1]);if(c)try{f=c.call(this,f[1])}catch(u){throw new h.Exception(f[1]);}return[l,f?f[1]:d]}},between:function(a,e,c){c=c||a;var d=f.each(f.ignore(a),e,f.ignore(c));return function(a){a=d.call(this,a);return[[a[0][0],r[0][2]],a[1]]}},list:function(a,e,c){e=e||f.rtoken(/^\s*/);c=c||null;return a instanceof Array?f.each(f.product(a.slice(0,-1),f.ignore(e)),a.slice(-1),f.ignore(c)):f.each(f.many(f.each(a,f.ignore(e))),px,f.ignore(c))},set:function(a,e,c){e=e||f.rtoken(/^\s*/);
c=c||null;return function(d){for(var k=null,n=k=null,l=null,p=[[],d],q=!1,s=0;s<a.length;s++){k=n=null;q=1===a.length;try{k=a[s].call(this,d)}catch(u){continue}l=[[k[0]],k[1]];if(0<k[1].length&&!q)try{n=e.call(this,k[1])}catch(v){q=!0}else q=!0;q||0!==n[1].length||(q=!0);if(!q){k=[];for(q=0;q<a.length;q++)s!==q&&k.push(a[q]);k=f.set(k,e).call(this,n[1]);0<k[0].length&&(l[0]=l[0].concat(k[0]),l[1]=k[1])}l[1].length<p[1].length&&(p=l);if(0===p[1].length)break}if(0===p[0].length)return p;if(c){try{n=
c.call(this,p[1])}catch(w){throw new h.Exception(p[1]);}p[1]=n[1]}return p}},forward:function(a,e){return function(c){return a[e].call(this,c)}},replace:function(a,e){return function(c){c=a.call(this,c);return[e,c[1]]}},process:function(a,e){return function(c){c=a.call(this,c);return[e.call(this,c[0]),c[1]]}},min:function(a,e){return function(c){var d=e.call(this,c);if(d[0].length<a)throw new h.Exception(c);return d}}},d=function(a){return function(){var e=null,c=[],d;1<arguments.length?e=Array.prototype.slice.call(arguments):
arguments[0]instanceof Array&&(e=arguments[0]);if(e){if(d=e.shift(),0<d.length)return e.unshift(d[void 0]),c.push(a.apply(null,e)),e.shift(),c}else return a.apply(null,arguments)}},c="optional not ignore cache".split(/\s/),a=0;a<c.length;a++)f[c[a]]=d(f[c[a]]);d=function(a){return function(){return arguments[0]instanceof Array?a.apply(null,arguments[0]):a.apply(null,arguments)}};c="each any all".split(/\s/);for(a=0;a<c.length;a++)f[c[a]]=d(f[c[a]])})();
(function(){var h=Date,f=function(c){for(var a=[],b=0;b<c.length;b++)c[b]instanceof Array?a=a.concat(f(c[b])):c[b]&&a.push(c[b]);return a},d=function(){if(this.meridian&&(this.hour||0===this.hour)){if("a"===this.meridian&&11<this.hour&&Date.Config.strict24hr)throw"Invalid hour and meridian combination";if("p"===this.meridian&&12>this.hour&&Date.Config.strict24hr)throw"Invalid hour and meridian combination";"p"===this.meridian&&12>this.hour?this.hour+=12:"a"===this.meridian&&12===this.hour&&(this.hour=
0)}};h.Translator={hour:function(c){return function(){this.hour=Number(c)}},minute:function(c){return function(){this.minute=Number(c)}},second:function(c){return function(){this.second=Number(c)}},secondAndMillisecond:function(c){return function(){var a=c.match(/^([0-5][0-9])\.([0-9]{1,3})/);this.second=Number(a[1]);this.millisecond=Number(a[2])}},meridian:function(c){return function(){this.meridian=c.slice(0,1).toLowerCase()}},timezone:function(c){return function(){var a=c.replace(/[^\d\+\-]/g,
"");a.length?this.timezoneOffset=Number(a):this.timezone=c.toLowerCase()}},day:function(c){var a=c[0];return function(){this.day=Number(a.match(/\d+/)[0]);if(1>this.day)throw"invalid day";}},month:function(c){return function(){this.month=3===c.length?"jan feb mar apr may jun jul aug sep oct nov dec".indexOf(c)/4:Number(c)-1;if(0>this.month)throw"invalid month";}},year:function(c){return function(){var a=Number(c);this.year=2<c.length?a:a+(a+2E3<Date.CultureInfo.twoDigitYearMax?2E3:1900)}},rday:function(c){return function(){switch(c){case "yesterday":this.days=
-1;break;case "tomorrow":this.days=1;break;case "today":this.days=0;break;case "now":this.days=0,this.now=!0}}},finishExact:function(c){c=c instanceof Array?c:[c];for(var a=0;a<c.length;a++)c[a]&&c[a].call(this);c=new Date;!this.hour&&!this.minute||this.month||this.year||this.day||(this.day=c.getDate());this.year||(this.year=c.getFullYear());this.month||0===this.month||(this.month=c.getMonth());this.day||(this.day=1);this.hour||(this.hour=0);this.minute||(this.minute=0);this.second||(this.second=
0);this.millisecond||(this.millisecond=0);d.call(this);if(this.day>h.getDaysInMonth(this.year,this.month))throw new RangeError(this.day+" is not a valid value for days.");c=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second,this.millisecond);100>this.year&&c.setFullYear(this.year);this.timezone?c.set({timezone:this.timezone}):this.timezoneOffset&&c.set({timezoneOffset:this.timezoneOffset});return c},finish:function(c){var a,b,e;c=c instanceof Array?f(c):[c];if(0===c.length)return null;
for(a=0;a<c.length;a++)"function"===typeof c[a]&&c[a].call(this);if(!this.now||this.unit||this.operator)c=this.now||-1!=="hour minute second".indexOf(this.unit)?new Date:h.today();else return new Date;a=!!(this.days&&null!==this.days||this.orient||this.operator);b="past"===this.orient||"subtract"===this.operator?-1:1;this.month&&"week"===this.unit&&(this.value=this.month+1,delete this.month,delete this.day);!this.month&&0!==this.month||-1==="year day hour minute second".indexOf(this.unit)||(this.value||
(this.value=this.month+1),this.month=null,a=!0);a||!this.weekday||this.day||this.days||(e=Date[this.weekday](),this.day=e.getDate(),this.month||(this.month=e.getMonth()),this.year=e.getFullYear());if(a&&this.weekday&&"month"!==this.unit&&"week"!==this.unit){var g=c;e=b||1;this.unit="day";this.days=(g=h.getDayNumberFromName(this.weekday)-g.getDay())?(g+7*e)%7:7*e}!this.weekday||"week"===this.unit||this.day||this.days||(e=Date[this.weekday](),this.day=e.getDate(),e.getMonth()!==c.getMonth()&&(this.month=
e.getMonth()));this.month&&"day"===this.unit&&this.operator&&(this.value||(this.value=this.month+1),this.month=null);null!=this.value&&null!=this.month&&null!=this.year&&(this.day=1*this.value);this.month&&!this.day&&this.value&&(c.set({day:1*this.value}),a||(this.day=1*this.value));this.month||!this.value||"month"!==this.unit||this.now||(this.month=this.value,a=!0);a&&(this.month||0===this.month)&&"year"!==this.unit&&(e=b||1,this.unit="month",this.months=(g=this.month-c.getMonth())?(g+12*e)%12:12*
e,this.month=null);this.unit||(this.unit="day");if(!this.value&&this.operator&&null!==this.operator&&this[this.unit+"s"]&&null!==this[this.unit+"s"])this[this.unit+"s"]=this[this.unit+"s"]+("add"===this.operator?1:-1)+(this.value||0)*b;else if(null==this[this.unit+"s"]||null!=this.operator)this.value||(this.value=1),this[this.unit+"s"]=this.value*b;d.call(this);!this.month&&0!==this.month||this.day||(this.day=1);if(!this.orient&&!this.operator&&"week"===this.unit&&this.value&&!this.day&&!this.month)return Date.today().setWeek(this.value);
if("week"===this.unit&&this.weeks&&!this.day&&!this.month)return c=Date[void 0!==this.weekday?this.weekday:"today"]().addWeeks(this.weeks),this.now&&c.setTimeToNow(),c;a&&this.timezone&&this.day&&this.days&&(this.day=this.days);a?c.add(this):c.set(this);this.timezone&&(this.timezone=this.timezone.toUpperCase(),a=h.getTimezoneOffset(this.timezone),c.hasDaylightSavingTime()&&(b=h.getTimezoneAbbreviation(a,c.isDaylightSavingTime()),b!==this.timezone&&(c.isDaylightSavingTime()?c.addHours(-1):c.addHours(1))),
c.setTimezoneOffset(a));return c}}})();
(function(){var h=Date;h.Grammar={};var f=h.Parsing.Operators,d=h.Grammar,c=h.Translator,a;a=function(){return f.each(f.any.apply(null,arguments),f.not(d.ctoken2("timeContext")))};d.datePartDelimiter=f.rtoken(/^([\s\-\.\,\/\x27]+)/);d.timePartDelimiter=f.stoken(":");d.whiteSpace=f.rtoken(/^\s*/);d.generalDelimiter=f.rtoken(/^(([\s\,]|at|@|on)+)/);var b={};d.ctoken=function(a){var c=b[a];if(!c){for(var c=Date.CultureInfo.regexPatterns,e=a.split(/\s+/),d=[],g=0;g<e.length;g++)d.push(f.replace(f.rtoken(c[e[g]]),
e[g]));c=b[a]=f.any.apply(null,d)}return c};d.ctoken2=function(a){return f.rtoken(Date.CultureInfo.regexPatterns[a])};var e=function(a,b,c,e){d[a]=e?f.cache(f.process(f.each(f.rtoken(b),f.optional(d.ctoken2(e))),c)):f.cache(f.process(f.rtoken(b),c))},g=function(a,b){return f.cache(f.process(d.ctoken2(a),b))},m={},k=function(a){m[a]=m[a]||d.format(a)[0];return m[a]};d.allformats=function(a){var b=[];if(a instanceof Array)for(var c=0;c<a.length;c++)b.push(k(a[c]));else b.push(k(a));return b};d.formats=
function(a){if(a instanceof Array){for(var b=[],c=0;c<a.length;c++)b.push(k(a[c]));return f.any.apply(null,b)}return k(a)};var n={timeFormats:function(){var a,b="h hh H HH m mm s ss ss.s z zz".split(" "),h=[/^(0[0-9]|1[0-2]|[1-9])/,/^(0[0-9]|1[0-2])/,/^([0-1][0-9]|2[0-3]|[0-9])/,/^([0-1][0-9]|2[0-3])/,/^([0-5][0-9]|[0-9])/,/^[0-5][0-9]/,/^([0-5][0-9]|[0-9])/,/^[0-5][0-9]/,/^[0-5][0-9]\.[0-9]{1,3}/,/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/,/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/],m=[c.hour,
c.hour,c.hour,c.hour,c.minute,c.minute,c.second,c.second,c.secondAndMillisecond,c.timezone,c.timezone];for(a=0;a<b.length;a++)e(b[a],h[a],m[a]);d.hms=f.cache(f.sequence([d.H,d.m,d.s],d.timePartDelimiter));d.t=g("shortMeridian",c.meridian);d.tt=g("longMeridian",c.meridian);d.zzz=g("timezone",c.timezone);d.timeSuffix=f.each(f.ignore(d.whiteSpace),f.set([d.tt,d.zzz]));d.time=f.each(f.optional(f.ignore(f.stoken("T"))),d.hms,d.timeSuffix)},dateFormats:function(){var b=function(){return f.set(arguments,
d.datePartDelimiter)},g,h="d dd M MM y yy yyy yyyy".split(" "),m=[/^([0-2]\d|3[0-1]|\d)/,/^([0-2]\d|3[0-1])/,/^(1[0-2]|0\d|\d)/,/^(1[0-2]|0\d)/,/^(\d+)/,/^(\d\d)/,/^(\d\d?\d?\d?)/,/^(\d\d\d\d)/],n=[c.day,c.day,c.month,c.month,c.year,c.year,c.year,c.year],k=["ordinalSuffix","ordinalSuffix"];for(g=0;g<h.length;g++)e(h[g],m[g],n[g],k[g]);d.MMM=d.MMMM=f.cache(f.process(d.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),c.month));d.ddd=d.dddd=f.cache(f.process(d.ctoken("sun mon tue wed thu fri sat"),
function(a){return function(){this.weekday=a}}));d.day=a(d.d,d.dd);d.month=a(d.M,d.MMM);d.year=a(d.yyyy,d.yy);d.mdy=b(d.ddd,d.month,d.day,d.year);d.ymd=b(d.ddd,d.year,d.month,d.day);d.dmy=b(d.ddd,d.day,d.month,d.year);d.date=function(a){return(d[Date.CultureInfo.dateElementOrder]||d.mdy).call(this,a)}},relative:function(){d.orientation=f.process(d.ctoken("past future"),function(a){return function(){this.orient=a}});d.operator=f.process(d.ctoken("add subtract"),function(a){return function(){this.operator=
a}});d.rday=f.process(d.ctoken("yesterday tomorrow today now"),c.rday);d.unit=f.process(d.ctoken("second minute hour day week month year"),function(a){return function(){this.unit=a}})}};d.buildGrammarFormats=function(){b={};n.timeFormats();n.dateFormats();n.relative();d.value=f.process(f.rtoken(/^([-+]?\d+)?(st|nd|rd|th)?/),function(a){return function(){this.value=a.replace(/\D/g,"")}});d.expression=f.set([d.rday,d.operator,d.value,d.unit,d.orientation,d.ddd,d.MMM]);d.format=f.process(f.many(f.any(f.process(f.rtoken(/^(dd?d?d?(?!e)|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),
function(a){if(d[a])return d[a];throw h.Parsing.Exception(a);}),f.process(f.rtoken(/^[^dMyhHmstz]+/),function(a){return f.ignore(f.stoken(a))}))),function(a){return f.process(f.each.apply(null,a),c.finishExact)});d._start=f.process(f.set([d.date,d.time,d.expression],d.generalDelimiter,d.whiteSpace),c.finish)};d.buildGrammarFormats();d._formats=d.formats('"yyyy-MM-ddTHH:mm:ssZ";yyyy-MM-ddTHH:mm:ss.sz;yyyy-MM-ddTHH:mm:ssZ;yyyy-MM-ddTHH:mm:ssz;yyyy-MM-ddTHH:mm:ss;yyyy-MM-ddTHH:mmZ;yyyy-MM-ddTHH:mmz;yyyy-MM-ddTHH:mm;ddd, MMM dd, yyyy H:mm:ss tt;ddd MMM d yyyy HH:mm:ss zzz;MMddyyyy;ddMMyyyy;Mddyyyy;ddMyyyy;Mdyyyy;dMyyyy;yyyy;Mdyy;dMyy;d'.split(";"));
d.start=function(a){try{var b=d._formats.call({},a);if(0===b[1].length)return b}catch(c){}return d._start.call({},a)}})();
(function(){var h=Date,f={removeOrds:function(d){return d=(ords=d.match(/\b(\d+)(?:st|nd|rd|th)\b/))&&2===ords.length?d.replace(ords[0],ords[1]):d},grammarParser:function(d){var c=null;try{c=h.Grammar.start.call({},d.replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1"))}catch(a){return null}return 0===c[1].length?c[0]:null},nativeFallback:function(d){var c;try{return(c=Date._parse(d))||0===c?new Date(c):null}catch(a){return null}}};h._parse||(h._parse=h.parse);h.parse=function(d){var c;if(!d)return null;if(d instanceof
Date)return d.clone();4<=d.length&&"0"!==d.charAt(0)&&"+"!==d.charAt(0)&&"-"!==d.charAt(0)&&(c=h.Parsing.ISO.parse(d)||h.Parsing.Numeric.parse(d));if(c instanceof Date&&!isNaN(c.getTime()))return c;d=h.Parsing.Normalizer.parse(f.removeOrds(d));c=f.grammarParser(d);return null!==c?c:f.nativeFallback(d)};Date.getParseFunction=function(d){var c=Date.Grammar.allformats(d);return function(a){for(var b=null,e=0;e<c.length;e++){try{b=c[e].call({},a)}catch(d){continue}if(0===b[1].length)return b[0]}return null}};
h.parseExact=function(d,c){return h.getParseFunction(c)(d)}})();
(function(){var h=Date,f=h.prototype,d=function(a,b){b||(b=2);return("000"+a).slice(-1*b)},c={d:"dd","%d":"dd",D:"ddd","%a":"ddd",j:"dddd",l:"dddd","%A":"dddd",S:"S",F:"MMMM","%B":"MMMM",m:"MM","%m":"MM",M:"MMM","%b":"MMM","%h":"MMM",n:"M",Y:"yyyy","%Y":"yyyy",y:"yy","%y":"yy",g:"h","%I":"h",G:"H",h:"hh",H:"HH","%H":"HH",i:"mm","%M":"mm",s:"ss","%S":"ss","%r":"hh:mm tt","%R":"H:mm","%T":"H:mm:ss","%X":"t","%x":"d","%e":"d","%D":"MM/dd/yy","%n":"\\n","%t":"\\t",e:"z",T:"z","%z":"z","%Z":"z",Z:"ZZ",
N:"u",w:"u","%w":"u",W:"W","%V":"W"},a={substitutes:function(a){return c[a]},interpreted:function(a,b){var c;switch(a){case "%u":return b.getDay()+1;case "z":return b.getOrdinalNumber();case "%j":return d(b.getOrdinalNumber(),3);case "%U":c=b.clone().set({month:0,day:1}).addDays(-1).moveToDayOfWeek(0);var f=b.clone().addDays(1).moveToDayOfWeek(0,-1);return f<c?"00":d((f.getOrdinalNumber()-c.getOrdinalNumber())/7+1);case "%W":return d(b.getWeek());case "t":return h.getDaysInMonth(b.getFullYear(),b.getMonth());
case "o":case "%G":return b.setWeek(b.getISOWeek()).toString("yyyy");case "%g":return b._format("%G").slice(-2);case "a":case "%p":return t("tt").toLowerCase();case "A":return t("tt").toUpperCase();case "u":return d(b.getMilliseconds(),3);case "I":return b.isDaylightSavingTime()?1:0;case "O":return b.getUTCOffset();case "P":return c=b.getUTCOffset(),c.substring(0,c.length-2)+":"+c.substring(c.length-2);case "B":return c=new Date,Math.floor((3600*c.getHours()+60*c.getMinutes()+c.getSeconds()+60*(c.getTimezoneOffset()+
60))/86.4);case "c":return b.toISOString().replace(/\"/g,"");case "U":return h.strtotime("now");case "%c":return t("d")+" "+t("t");case "%C":return Math.floor(b.getFullYear()/100+1)}},shouldOverrideDefaults:function(a){switch(a){case "%e":return!0;default:return!1}},parse:function(b,c){var d,f=c||new Date;return(d=a.substitutes(b))?d:(d=a.interpreted(b,f))?d:b}};h.normalizeFormat=function(b,c){return b.replace(/(%|\\)?.|%%/g,function(b){return a.parse(b,c)})};h.strftime=function(a,b){return Date.parse(b)._format(a)};
h.strtotime=function(a){a=h.parse(a);return Math.round(h.UTC(a.getUTCFullYear(),a.getUTCMonth(),a.getUTCDate(),a.getUTCHours(),a.getUTCMinutes(),a.getUTCSeconds(),a.getUTCMilliseconds())/1E3)};var b=function(b){return function(c){var d=!1;if("\\"===c.charAt(0)||"%%"===c.substring(0,2))return c.replace("\\","").replace("%%","%");d=a.shouldOverrideDefaults(c);if(c=h.normalizeFormat(c,b))return b.toString(c,d)}};f._format=function(a){var c=b(this);return a?a.replace(/(%|\\)?.|%%/g,c):this._toString()};
f.format||(f.format=f._format)})();
(function(){var h=function(c){return function(){return this[c]}},f=function(c){return function(a){this[c]=a;return this}},d=function(c,a,b,e,f){if(1===arguments.length&&"number"===typeof c){var h=0>c?-1:1,k=Math.abs(c);this.setDays(Math.floor(k/864E5)*h);k%=864E5;this.setHours(Math.floor(k/36E5)*h);k%=36E5;this.setMinutes(Math.floor(k/6E4)*h);k%=6E4;this.setSeconds(Math.floor(k/1E3)*h);this.setMilliseconds(k%1E3*h)}else this.set(c,a,b,e,f);this.getTotalMilliseconds=function(){return 864E5*this.getDays()+
36E5*this.getHours()+6E4*this.getMinutes()+1E3*this.getSeconds()};this.compareTo=function(a){var b=new Date(1970,1,1,this.getHours(),this.getMinutes(),this.getSeconds());a=null===a?new Date(1970,1,1,0,0,0):new Date(1970,1,1,a.getHours(),a.getMinutes(),a.getSeconds());return b<a?-1:b>a?1:0};this.equals=function(a){return 0===this.compareTo(a)};this.add=function(a){return null===a?this:this.addSeconds(a.getTotalMilliseconds()/1E3)};this.subtract=function(a){return null===a?this:this.addSeconds(-a.getTotalMilliseconds()/
1E3)};this.addDays=function(a){return new d(this.getTotalMilliseconds()+864E5*a)};this.addHours=function(a){return new d(this.getTotalMilliseconds()+36E5*a)};this.addMinutes=function(a){return new d(this.getTotalMilliseconds()+6E4*a)};this.addSeconds=function(a){return new d(this.getTotalMilliseconds()+1E3*a)};this.addMilliseconds=function(a){return new d(this.getTotalMilliseconds()+a)};this.get12HourHour=function(){return 12<this.getHours()?this.getHours()-12:0===this.getHours()?12:this.getHours()};
this.getDesignator=function(){return 12>this.getHours()?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator};this.toString=function(a){this._toString=function(){return null!==this.getDays()&&0<this.getDays()?this.getDays()+"."+this.getHours()+":"+this.p(this.getMinutes())+":"+this.p(this.getSeconds()):this.getHours()+":"+this.p(this.getMinutes())+":"+this.p(this.getSeconds())};this.p=function(a){return 2>a.toString().length?"0"+a:a};var b=this;return a?a.replace(/dd?|HH?|hh?|mm?|ss?|tt?/g,
function(a){switch(a){case "d":return b.getDays();case "dd":return b.p(b.getDays());case "H":return b.getHours();case "HH":return b.p(b.getHours());case "h":return b.get12HourHour();case "hh":return b.p(b.get12HourHour());case "m":return b.getMinutes();case "mm":return b.p(b.getMinutes());case "s":return b.getSeconds();case "ss":return b.p(b.getSeconds());case "t":return(12>b.getHours()?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator).substring(0,1);case "tt":return 12>b.getHours()?Date.CultureInfo.amDesignator:
Date.CultureInfo.pmDesignator}}):this._toString()};return this};(function(c,a){for(var b=0;b<a.length;b++){var e=a[b],d=e.slice(0,1).toUpperCase()+e.slice(1);c.prototype[e]=0;c.prototype["get"+d]=h(e);c.prototype["set"+d]=f(e)}})(d,"years months days hours minutes seconds milliseconds".split(" ").slice(2));d.prototype.set=function(c,a,b,e,d){this.setDays(c||this.getDays());this.setHours(a||this.getHours());this.setMinutes(b||this.getMinutes());this.setSeconds(e||this.getSeconds());this.setMilliseconds(d||
this.getMilliseconds())};Date.prototype.getTimeOfDay=function(){return new d(0,this.getHours(),this.getMinutes(),this.getSeconds(),this.getMilliseconds())};Date.TimeSpan=d;"undefined"!==typeof window&&(window.TimeSpan=d)})();
(function(){var h=function(a){return function(){return this[a]}},f=function(a){return function(b){this[a]=b;return this}},d=function(a,b,c,d){function f(){b.addMonths(-a);d.months++;12===d.months&&(d.years++,d.months=0)}if(1===a)for(;b>c;)f();else for(;b<c;)f();d.months--;d.months*=a;d.years*=a},c=function(a,b,c,f,h,k,n){if(7===arguments.length)this.set(a,b,c,f,h,k,n);else if(2===arguments.length&&arguments[0]instanceof Date&&arguments[1]instanceof Date){var l=arguments[0].clone(),p=arguments[1].clone(),
q=l>p?1:-1;this.dates={start:arguments[0].clone(),end:arguments[1].clone()};d(q,l,p,this);var s=!1===(l.isDaylightSavingTime()===p.isDaylightSavingTime());s&&1===q?l.addHours(-1):s&&l.addHours(1);l=p-l;0!==l&&(l=new TimeSpan(l),this.set(this.years,this.months,l.getDays(),l.getHours(),l.getMinutes(),l.getSeconds(),l.getMilliseconds()))}return this};(function(a,b){for(var c=0;c<b.length;c++){var d=b[c],m=d.slice(0,1).toUpperCase()+d.slice(1);a.prototype[d]=0;a.prototype["get"+m]=h(d);a.prototype["set"+
m]=f(d)}})(c,"years months days hours minutes seconds milliseconds".split(" "));c.prototype.set=function(a,b,c,d,f,h,n){this.setYears(a||this.getYears());this.setMonths(b||this.getMonths());this.setDays(c||this.getDays());this.setHours(d||this.getHours());this.setMinutes(f||this.getMinutes());this.setSeconds(h||this.getSeconds());this.setMilliseconds(n||this.getMilliseconds())};Date.TimePeriod=c;"undefined"!==typeof window&&(window.TimePeriod=c)})();
@@ -218,7 +218,7 @@
$form.attr('action', this.href || routePath('preview'));
$form.attr('target', '_blank');
var paths = window.location.pathname.split('/');
$form.attr('page', paths[ paths.length - 1 ] || '')
$form.attr('page', decodeURIComponent(paths[ paths.length - 1 ]) || '')
$form.submit();
@@ -96,6 +96,13 @@ function preparePage () {
}
}
function getLocalTime(datetime_string, format) {
if (format === undefined) { format = 'Y-m-d %H:%M:%S O'; }
var date = new Date(datetime_string);
return date.format(format);
}
function flashNotice(type, notice, button_label, button_function, button_type) {
// accepted types: info, success, warn, error
nested_button_html = '';
@@ -155,13 +162,23 @@ $(document).ready(function() {
}
}
// Set time in local time
if (showLocalTime) {
$(function() {
$('time').each(function() {
var datetime = $(this).attr('datetime');
var format = $(this).data('format');
$(this).html(getLocalTime(datetime, format));
});
});
}
if ($('#minibutton-upload-page').length) {
if ($('.minibutton-upload-page').length) {
new ClipboardJS('#ClipboardJSlink');
$('#minibutton-upload-page').parent().removeClass('jaws');
$('#minibutton-upload-page').click(function(e) {
$('.minibutton-upload-page').parent().removeClass('jaws');
$('.minibutton-upload-page').click(function(e) {
e.preventDefault();
$.GollumDialog.init({
title: 'Upload File',
fields: [
@@ -213,9 +230,9 @@ $(document).ready(function() {
});
}
if ($('#minibutton-rename-page').length) {
$('#minibutton-rename-page').parent().removeClass('jaws');
$('#minibutton-rename-page').click(function(e) {
if ($('.minibutton-rename-page').length) {
$('.minibutton-rename-page').parent().removeClass('jaws');
$('.minibutton-rename-page').click(function(e) {
e.preventDefault();
var path = decodeURI(pagePath());
@@ -257,9 +274,9 @@ $(document).ready(function() {
});
}
if ($('#minibutton-new-page').length) {
$('#minibutton-new-page').parent().removeClass('jaws');
$('#minibutton-new-page').click(function(e) {
if ($('.minibutton-new-page').length) {
$('.minibutton-new-page').parent().removeClass('jaws');
$('.minibutton-new-page').click(function(e) {
e.preventDefault();
var path = pagePath();
if( path === undefined && $('#file-browser').length != 0 ){
@@ -345,7 +362,7 @@ $(document).ready(function() {
var formData = new FormData($('#gollum-editor-form').get(0));
var paths = window.location.pathname.split('/');
var sectionAnchor = window.location.hash.substr(1);
formData.append('page', paths[ paths.length - 1 ] || '')
formData.append('page', decodeURIComponent(paths[ paths.length - 1 ]) || '')
$.ajax({
url: routePath('preview'),
data: formData,
@@ -380,8 +397,12 @@ $(document).ready(function() {
active_tab = '#edit.tabnav-tab';
}
$('.tabnav-tab.selected').removeAttr('aria-current');
$('.tabnav-tab.selected').removeClass('selected');
$(active_tab).attr('aria-current', 'page');
$(active_tab).addClass('selected');
$('.tabnav-div').hide();
$(active_div).show();
}
@@ -570,8 +591,9 @@ $(document).ready(function() {
url: routePath('last_commit_info'),
data: {path: $("#page-info-toggle").data('pagepath')},
success: function ( data ) {
var date = showLocalTime ? getLocalTime(data.date) : data.date;
$("#last-edit").next(".dotted-spinner").toggleClass('hidden');
$("#last-edit-in-progress").html('Last edited by <b>' + data.author + '</b>, ' + data.date);
$("#last-edit-in-progress").html('Last edited by <b>' + data.author + '</b>, ' + date);
}
});
$("#last-edit").next(".dotted-spinner").toggleClass('hidden')
@@ -0,0 +1,6 @@
// Polyfill to support Internet Explorer
if (!Array.prototype.includes) {
Array.prototype.includes = function (x) {
return 0 <= this.indexOf(x)
};
}
@@ -1,83 +0,0 @@
// Describes base page layout
/* @section wrapper */
#wiki-wrapper {
margin: 0 auto;
overflow: visible;
width: 100%;
@include desktop-breakpoint {
@include desktop-page-layout;
}
}
/* @section content */
#wiki-content {
height: 1%;
overflow: visible;
.wrap {
height: 1%;
overflow: auto;
}
&.uploading {
opacity: 0.5;
}
p, li {
code {
white-space:pre-wrap;
word-wrap:break-word;
}
}
}
/* @section body */
#wiki-body {
display: block;
width: 100%;
margin-right: 3%;
@include largemobile-breakpoint {
float: left;
clear: left;
.has-sidebar & {
width: $layout-with-sidebar - $layout-with-sidebar-leeway;
}
.has-leftbar & {
float: right;
clear: right;
}
}
}
/* @section sidebar */
#wiki-sidebar {
@include largemobile-breakpoint {
width: $layout-sidebar;
.has-leftbar & {
float: left;
}
.has-rightbar & {
float: right;
}
}
}
/* @section footer */
#wiki-footer {
clear: both;
@include largemobile-breakpoint {
.has-sidebar & {
width: $layout-with-sidebar;
}
}
}
@@ -147,7 +147,3 @@
}
}
}
#gollum-dialog-dialog-buttons {
overflow: hidden;
}
@@ -29,6 +29,7 @@ a.tabnav-tab:focus {
overflow: hidden;
font-family: Consolas, "Liberation Mono", Courier, monospace;
font-size: 1em;
padding: 0;
}
#gollum-editor {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -53,19 +53,85 @@ a {
/* Markdown body */
.header-enum {
h2 {counter-reset: h3}
h3 {counter-reset: h4}
h4 {counter-reset: h5}
h5 {counter-reset: h6}
--header-enum-style: decimal;
h1:before {counter-increment: h1; content: counter(h1, var(--header-enum-style)) ". ";}
h2:before {counter-increment: h2; content: counter(h1, var(--header-enum-style)) "." counter(h2, var(--header-enum-style)) ". ";}
h3:before {counter-increment: h3; content: counter(h1, var(--header-enum-style)) "." counter(h2, var(--header-enum-style)) "." counter(h3, var(--header-enum-style)) ". ";}
h4:before {counter-increment: h4; content: counter(h1, var(--header-enum-style)) "." counter(h2, var(--header-enum-style)) "." counter(h3, var(--header-enum-style)) "." counter(h4, var(--header-enum-style)) ". ";}
h5:before {counter-increment: h5; content: counter(h1, var(--header-enum-style)) "." counter(h2, var(--header-enum-style)) "." counter(h3, var(--header-enum-style)) "." counter(h4, var(--header-enum-style)) "." counter(h5, var(--header-enum-style)) ". ";}
h6:before {counter-increment: h6; content: counter(h1, var(--header-enum-style)) "." counter(h2, var(--header-enum-style)) "." counter(h3, var(--header-enum-style)) "." counter(h4, var(--header-enum-style)) "." counter(h5, var(--header-enum-style)) "." counter(h6, var(--header-enum-style)) ". ";}
h1 {
counter-increment: h1;
counter-reset: h2;
&:before {
content: counter(h1, var(--header-enum-style)) ". ";
}
}
h2 {
counter-increment: h2;
counter-reset: h3;
&:before {
content:
counter(h1, var(--header-enum-style))
"." counter(h2, var(--header-enum-style))
". ";
}
}
h3 {
counter-reset: h4;
counter-increment: h3;
&:before {
content:
counter(h1, var(--header-enum-style))
"." counter(h2, var(--header-enum-style))
"." counter(h3, var(--header-enum-style))
". ";
}
}
h4 {
counter-increment: h4;
counter-reset: h5;
&:before {
content:
counter(h1, var(--header-enum-style))
"." counter(h2, var(--header-enum-style))
"." counter(h3, var(--header-enum-style))
"." counter(h4, var(--header-enum-style))
". ";
}
}
h5 {
counter-increment: h5;
counter-reset: h6;
&:before {
content:
counter(h1, var(--header-enum-style))
"." counter(h2, var(--header-enum-style))
"." counter(h3, var(--header-enum-style))
"." counter(h4, var(--header-enum-style))
"." counter(h5, var(--header-enum-style))
". ";
}
}
h6 {
counter-increment: h6;
&:before {
content:
counter(h1, var(--header-enum-style))
"." counter(h2, var(--header-enum-style))
"." counter(h3, var(--header-enum-style))
"." counter(h4, var(--header-enum-style))
"." counter(h5, var(--header-enum-style))
"." counter(h6, var(--header-enum-style))
". ";
}
}
}
#footer {
@@ -77,43 +143,58 @@ a {
padding: 1em 1em 1em 0;
font-size: 15px;
line-height: 1.7;
overflow: hidden;
word-wrap: break-word;
/* MediaWiki's TOC table -- this does not pertain to gollum's own TOC functionality */
table.toc {
width: auto;
display: inline-table;
.anchor {
display: none;
}
}
.anchor {
display: inline-block;
position: absolute;
opacity: 0;
background: url('data:image/svg+xml;utf8,<%= rocticon_css(:link) %>') no-repeat;
background-size: 0.6em 1.35em;
padding-right: 0.5em;
padding-top: 0.4em;
margin-left: -0.8em;
width: 1em;
height: 1em;
text-decoration: none;
transition-property: opacity;
transition: 0.1s;
}
h1,
h2,
h3,
h4,
h5,
h6 {
$anchor-icon-size: 20px;
*:hover > .anchor, .anchor:focus{
opacity: 1;
}
position: relative;
.anchor.edit {
margin-left: 2em !important;
margin-top: 0.5em;
height: 0.5em;
background: url('data:image/svg+xml;utf8,<%= rocticon_css(:pencil) %>') no-repeat;
.anchor {
display: inline-block;
position: absolute;
margin-top: 0.1em;
width: $anchor-icon-size;
text-decoration: none;
opacity: 0;
transition: opacity 0.1s ease-in-out;
&:before {
content: url('data:image/svg+xml;utf8,<%= rocticon_css(:link) %>');
}
&:not(.edit) {
left: -($anchor-icon-size/2);
}
&.edit {
margin-left: ($anchor-icon-size/2);
&:before {
content: url('data:image/svg+xml;utf8,<%= rocticon_css(:pencil) %>');
}
}
}
&:hover > .anchor,
.anchor:focus {
opacity: 1;
}
}
a {
@@ -125,13 +206,6 @@ a {
color: inherit;
text-decoration: none;
}
&:first-child {
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
padding-top: 0;
}
}
}
> *:first-child {
@@ -166,18 +240,6 @@ a {
border-bottom: 1px solid #eee;
}
h3 {
}
h4 {
}
h5 {
}
h6 {
}
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
@@ -448,6 +510,14 @@ a {
border: none;
}
#head .header-title {
font-size: 1.5em;
@include largemobile-breakpoint {
font-size: 32px;
}
}
/* Highlights */
.highlight {
@@ -615,6 +685,10 @@ a {
color: #999;
background-color: #EAF2F5;
}
.gg {
color: #000000a0;
}
}
.type-csharp {
@@ -639,15 +713,33 @@ a {
.s, .sc {
color: #A31515;
}
}
}
}
div.pagination a.disabled {
pointer-events: none;
}
nav.actions {
scrollbar-width: none;
-ms-overflow-style: -ms-autohiding-scrollbar;
::webkit-scrollbar {
display: none;
}
}
.search-results {
.search-context li:nth-child(n+4) {
display: none;
}
}
}
/* @section user */
#user p {
text-align: right;
padding-right:0.5em;
font-size: .8em;
line-height: 2.0em;
color: #999;
}
+4 -2
View File
@@ -1,7 +1,9 @@
<div id="wiki-wrapper" class="compare">
<div id="head">
<h1 class="py-4">{{message}}</h1>
{{author}} commited {{authored_date}}
<h1 class="header-title text-center text-md-left py-4">
{{message}}
</h1>
{{author}} commited <time datetime="{{datetime}}">{{authored_date}}</time>
<span class="px-2 float-right">commit <code>{{version}}</code></span>
</div>
+10 -4
View File
@@ -1,8 +1,14 @@
<div id="wiki-wrapper" class="compare">
<div id="head">
{{>navbar}}
<h1 class="py-4"><span class="f1-light text-gray-light">Comparing versions of</span> {{name}}</h1>
</div>
<div id="head">
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
Comparing versions of
</span>
{{name}}
</h1>
</div>
{{#message}}
<p>{{message}}</p>
+16 -11
View File
@@ -1,16 +1,21 @@
<div id="wiki-wrapper" class="create">
<div id="head">
{{>navbar}}
<h1 class="py-4">Create New Page</h1>
</div>
<div id="head">
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
Create New Page
</h1>
</div>
<div id="wiki-content" class="create edit">
<div class="tabnav">
<nav class="tabnav-tabs" aria-label="Foo bar">
<a href="#" id="edit" class="tabnav-tab selected" aria-current="edit">Edit</a>
<a href="#" id="preview" class="tabnav-tab" aria-current="preview">Preview</a>
</nav>
</div>
<div class="tabnav">
<nav class="tabnav-tabs" aria-label="Toggle edit or preview mode">
<a href="#" id="edit" class="tabnav-tab selected" aria-current="page">
Edit
</a>
<a href="#" id="preview" class="tabnav-tab">Preview</a>
</nav>
</div>
<div class="has-sidebar tabnav-div" id="edit-content">
{{>editor}}
</div>
+11 -6
View File
@@ -1,12 +1,17 @@
<div id="wiki-wrapper" class="edit">
<div id="head">
{{>navbar}}
<h1 class="py-4">Editing <strong>{{title}}</strong></h1>
</div>
<div id="head">
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
Editing <strong>{{title}}</strong>
</h1>
</div>
<div class="tabnav">
<nav class="tabnav-tabs">
<a href="#" id="edit" class="tabnav-tab selected" aria-current="edit">Edit</a>
<a href="#" id="preview" class="tabnav-tab" aria-current="preview">Preview</a>
<a href="#" id="edit" class="tabnav-tab selected" aria-current="page">
Edit
</a>
<a href="#" id="preview" class="tabnav-tab">Preview</a>
</nav>
</div>
<div class="tabnav-div" id="edit-content">{{>editor}}</div>
+3 -3
View File
@@ -37,7 +37,7 @@
<button class="btn btn-sm function-button" id="function-h3" title="Heading 3">h3</button>
<span class="pr-2"></span>
<button class="btn btn-sm function-button" id="function-link" title="Link">{{#octicon}}link{{/octicon}}</button>
<button class="btn btn-sm function-button" id="function-image" title="Image">{{#octicon}}file-media{{/octicon}}</button>
<button class="btn btn-sm function-button" id="function-image" title="Image">{{#octicon}}image{{/octicon}}</button>
<span class="pr-2"></span>
{{#critic_markup}}
<button class="btn btn-sm function-button" id="function-critic-accept" title="Accept Selected CriticMarkup">{{#octicon}}plus{{/octicon}}</button>
@@ -50,13 +50,13 @@
<div id="gollum-editor-format-selector">
<label for="format">Keybinding</label>
<select id="keybinding" name="keybinding" class="form-select">
<select id="keybinding" name="keybinding" class="form-select input-sm">
<option selected="selected">default</option>
<option>vim</option>
<option>emacs</option>
</select>
<label for="format">Markup</label>
<select id="wiki_format" name="format" class="form-select">
<select id="wiki_format" name="format" class="form-select input-sm">
{{#formats}}
{{#enabled}}
<option {{#selected}}selected="selected" {{/selected}}value="{{id}}" data-ext="{{ext}}">
+10 -3
View File
@@ -1,8 +1,15 @@
<div id="wiki-wrapper" class="history">
<div id="head">
{{>navbar}}
<h1 class="py-4"><span class="f1-light text-gray-light">History for</span> {{name}}</h1>
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
History for
</span>
{{name}}
</h1>
</div>
<div id="page-history">
{{>pagination}}
@@ -16,7 +23,7 @@
<li class="Box-row border-top Box-row--hover-gray d-flex flex-items-center">
<span class="pr-2"><input class="checkbox" type="checkbox" name="versions[]" value="{{id}}"></span>
<span class="float-left col-2" id="user-icons">{{>author_template}}</span>
<span class="flex-auto col-1 text-gray-light">{{date}}</span>
<time class="flex-auto col-1 text-gray-light" datetime="{{datetime}}" data-format="{{date_format}}">{{date}}</time>
<span class="flex-auto col-5">{{message}}</span>
<span class="pl-4 float-right">
<a href="{{href}}" class="btn btn-outline text-mono">{{id7}}</a>
+9 -6
View File
@@ -1,8 +1,11 @@
<div id="wiki-wrapper" class="history">
<div id="head">
{{>navbar}}
<h1 class="py-4">{{title}}</h1>
</div>
<div id="head">
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
{{title}}
</h1>
</div>
{{>pagination}}
@@ -12,8 +15,8 @@
{{#versions}}
<li class="Box-row Box-row--hover-gray border-top d-flex flex-items-center">
<span class="float-left col-2" id="user-icons">{{>author_template}}</span>
<span class="flex-auto col-1 text-gray-light">{{date}}</span>
<span class="flex-auto col-7">{{message}}<br/>
<time class="flex-auto col-1 text-gray-light" datetime="{{datetime}}" data-format="{{date_format}}">{{date}}</time>
<span class="flex-auto col-5">{{message}}<br/>
{{#files}}
<span class="flex-auto col-2">{{#renamed}}{{renamed}} -> {{/renamed}}<a href="{{link}}">{{file}}</a></span><br/>
{{/files}}
+2 -1
View File
@@ -11,10 +11,10 @@
{{#css}}<link rel="stylesheet" type="text/css" href="{{custom_css}}" media="all">{{/css}}
{{#noindex}}<meta name="robots" content="noindex, nofollow" />{{/noindex}}
<script>
var criticMarkup = '{{critic_markup}}';
var baseUrl = '{{base_url}}';
var showLocalTime = {{{show_local_time}}};
var uploadDest = 'uploads';
var perPageUploads = '{{per_page_uploads}}';
if (perPageUploads == 'true') {
@@ -60,6 +60,7 @@
<body>
<div class="container-lg clearfix">
{{{yield}}}
{{< user}}
</div>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
<details class="details-reset details-overlay">
<summary class="btn btn-invisible" aria-haspopup="true">
<span aria-label="Open menu">☰</span>
</summary>
<div class="SelectMenu mx-sm-2">
<div class="SelectMenu-modal">
<div class="SelectMenu-divider py-3">
<h2 class="h6">Current Page</h2>
<div>{{page_header}}</div>
</div>
{{#history}}
<a
class="SelectMenu-item"
href="{{history_path}}/{{escaped_url_path}}"
role="menuitem"
>
<span>History</span>
</a>
{{/history}}
{{#allow_editing}}
{{#allow_uploads}}
<a class="SelectMenu-item minibutton-upload-page" role="menuitem">
<span>Upload</span>
</a>
{{/allow_uploads}}
{{#editable}}
<a class="SelectMenu-item minibutton-rename-page" role="menuitem">
<span>Rename</span>
</a>
<a
class="SelectMenu-item minibutton-edit-page"
href="{{edit_path}}/{{escaped_url_path}}"
role="menuitem"
>
<span>Edit</span>
</a>
{{/editable}}
{{/allow_editing}}
<div class="SelectMenu-divider py-3">
<h2 class="h6">Main Menu</h2>
</div>
<div class="SelectMenu-list">
<a class="SelectMenu-item" role="menuitem" href="{{page_route}}">
Home
</a>
{{#overview}}
<a class="SelectMenu-item" role="menuitem" href="{{overview_path}}">
Overview
</a>
{{/overview}}
{{#latest_changes}}
<a
class="SelectMenu-item"
href="{{latest_changes_path}}"
role="menuitem"
>
Latest Changes
</a>
{{/latest_changes}}
</div>
</div>
</div>
</details>
+108 -41
View File
@@ -1,43 +1,110 @@
<nav class="actions pt-4">
<div class="TableObject">
<div class="TableObject-item">
<a class="btn" id="minibutton-home" href="{{page_route}}">Home</a>
</div>
<nav class="TableObject
actions
border-bottom
border-md-0
p-2
pt-lg-4
px-lg-0
overflow-x-scroll">
<div class="TableObject-item hide-lg hide-xl">
{{>mobilenav}}
</div>
<div class="TableObject-item hide-sm hide-md">
<a class="btn btn-sm" id="minibutton-home" href="{{page_route}}">
Home
</a>
</div>
<div class="TableObject-item TableObject-item--primary px-2" {{^search}}style="visibility:hidden"{{/search}}>
{{>searchbar}}
</div>
<div class="TableObject-item">
{{#overview}}<a class="btn" id="minibutton-overview" href="{{overview_path}}">Overview</a>{{/overview}}
{{#latest_changes}}<a class="btn" id="minibutton-latest-changes" href="{{latest_changes_path}}">Latest Changes</a>{{/latest_changes}}
</div>
{{#history}}
<div class="TableObject-item pl-1">
<a class="btn" id="minibutton-history" href="{{history_path}}/{{escaped_url_path}}">Page History</a>
</div>
{{/history}}
{{#allow_editing}}
<div class="TableObject-item pl-1">
{{#allow_uploads}}
<a class="btn" id="minibutton-upload-page" href="#">Upload</a>
{{/allow_uploads}}
{{#editable}}
<a class="btn" id="minibutton-rename-page" href="#">Rename</a>
<a class="btn" id="minibutton-edit-page" href="{{edit_path}}/{{escaped_url_path}}">Edit</a>
<a class="btn btn-primary" id="minibutton-new-page" href="#">New</a>
{{/editable}}
{{^editable}}
{{#newable}}
<a class="btn btn-primary" id="minibutton-new-page" href="#">New</a>
{{/newable}}
{{/editable}}
</div>
{{/allow_editing}}
</div>
</nav>
<div
class="TableObject-item TableObject-item--primary px-2"
{{^search}}style="visibility:hidden"{{/search}}
>
{{>searchbar}}
</div>
<div class="TableObject-item hide-sm hide-md">
<div class="BtnGroup d-flex">
{{#overview}}
<a
class="btn BtnGroup-item btn-sm"
href="{{overview_path}}"
id="minibutton-overview"
>
Overview
</a>
{{/overview}}
{{#latest_changes}}
<a
class="btn BtnGroup-item btn-sm"
href="{{latest_changes_path}}"
id="minibutton-latest-changes"
>
Latest Changes
</a>
{{/latest_changes}}
</div>
</div>
<div class="TableObject-item px-2">
<div class="BtnGroup d-flex">
{{#history}}
<a
class="btn BtnGroup-item btn-sm hide-sm hide-md"
href="{{history_path}}/{{escaped_url_path}}/{{version}}"
id="minibutton-history"
>
History
</a>
{{/history}}
{{#allow_editing}}
{{#allow_uploads}}
<button
class="btn BtnGroup-item btn-sm hide-sm hide-md
minibutton-upload-page"
>
Upload
</button>
{{/allow_uploads}}
{{#editable}}
<button
class="btn BtnGroup-item btn-sm hide-sm hide-md
minibutton-rename-page"
>
Rename
</button>
<a
class="btn BtnGroup-item btn-sm hide-sm hide-md"
href="{{edit_path}}/{{escaped_url_path}}"
id="minibutton-edit-page"
>
Edit
</a>
{{/editable}}
{{/allow_editing}}
</div>
</div>
{{#allow_editing}}
{{#editable}}
<div class="TableObject-item">
<a class="btn btn-primary btn-sm minibutton-new-page" href="#">
New
</a>
</div>
{{/editable}}
{{^editable}}
{{#newable}}
<div class="TableObject-item">
<a class="btn btn-primary btn-sm minibutton-new-page" href="#">
New
</a>
</div>
{{/newable}}
{{/editable}}
{{/allow_editing}}
</nav>
+8 -5
View File
@@ -1,8 +1,11 @@
<div id="wiki-wrapper" class="results">
<div id="head" class="overview">
{{>navbar}}
<h1 class="py-4">{{title}}</h1>
</div>
<div id="head" class="overview">
{{>navbar}}
<h1 class="header-title text-center text-md-left py-4">
{{title}}
</h1>
</div>
<div id="overview">
{{#has_results}}
@@ -18,7 +21,7 @@
<span class="pr-2">{{{icon}}}</span>
<span><a href="{{url}}">{{name}}</a></span>
{{#allow_editing}}
{{#is_file}}<button class="btn btn-sm float-right delete-file" data-file-path="{{url}}" data-confirm="Are you sure you want to delete {{name}}?">{{#octicon}}trashcan{{/octicon}}</button>{{/is_file}}
{{#is_file}}<button class="btn btn-sm float-right delete-file" data-file-path="{{file_path}}" data-confirm="Are you sure you want to delete {{name}}?">{{#octicon}}trash{{/octicon}}</button>{{/is_file}}
{{/allow_editing}}
</li>
{{/files_folders}}
+7 -1
View File
@@ -1,7 +1,13 @@
<div id="wiki-wrapper" class="results">
<div id="head">
{{>navbar}}
<h1 class="py-4"><span class="f1-light text-gray-light">Search results for</span> {{name}}</h1>
<h1 class="header-title text-center text-md-left py-4">
<span class="f1-light text-gray-light">
Search results for
</span>
{{name}}
</h1>
</div>
{{#has_results}}
+10
View File
@@ -0,0 +1,10 @@
<div id="user">
<p>
{{#user_authed}}
{{user_name}} | {{user_provider}} | <strong><a href="/__omnigollum__/logout">[Logout]</a></strong>
{{/user_authed}}
{{^user_authed}}
not logged in | <strong><a href="/__omnigollum__/login">[Login]</a></strong>
{{/user_authed}}
<p>
</div>
+21 -18
View File
@@ -1,7 +1,8 @@
<div id="wiki-content">
<h1 class="pt-4">{{page_header}}</h1>
<div id="wiki-content" class="px-2 px-lg-0">
<h1 class="header-title text-center text-md-left pt-4">
{{page_header}}
</h1>
<div class="breadcrumb">{{{breadcrumb}}}</div>
<div class="{{#has_header}}has-header{{/has_header}}{{#has_footer}} has-footer{{/has_footer}}{{#has_sidebar}} has-sidebar has-{{bar_side}}bar{{/has_sidebar}}{{#has_toc}} has-toc{{/has_toc}}">
{{#has_toc}}
@@ -9,14 +10,7 @@
{{{toc_content}}}
</div>
{{/has_toc}}
{{#has_sidebar}}
<div id="wiki-sidebar" class="gollum-{{sidebar_format}}-content">
<div id="sidebar-content" class="Box Box--condensed col-3 markdown-body px-4 float-{{bar_side}}">
{{{sidebar_content}}}
</div>
</div>
{{/has_sidebar}}
<div id="wiki-body" class="gollum-{{format}}-content overflow-hidden {{#left_bar}}pl-4{{/left_bar}}">
<div id="wiki-body" class="gollum-{{format}}-content">
{{#has_header}}
<div id="wiki-header" class="gollum-{{header_format}}-content">
<div id="header-content" class="markdown-body">
@@ -24,14 +18,23 @@
</div>
</div>
{{/has_header}}
<div class="markdown-body {{#header_enum?}}header-enum{{/header_enum?}}" {{#header_enum?}}style="--header-enum-style:{{header_enum_style}};"{{/header_enum?}}>
{{{rendered_metadata}}}
{{{content}}}
<div class="main-content clearfix container-lg">
<div class="markdown-body {{#header_enum?}}header-enum{{/header_enum?}} {{#has_sidebar}}float-md-{{body_side}} col-md-9{{/has_sidebar}}" {{#header_enum?}}style="--header-enum-style:{{header_enum_style}};"{{/header_enum?}}>
{{{rendered_metadata}}}
{{{content}}}
</div>
{{#has_sidebar}}
<div id="wiki-sidebar" class="Box Box--condensed float-md-{{body_side}} col-md-3">
<div id="sidebar-content" class="gollum-{{sidebar_format}}-content markdown-body px-4">
{{{sidebar_content}}}
</div>
</div>
{{/has_sidebar}}
</div>
</div>
{{#has_footer}}
<div id="wiki-footer" class="gollum-{{footer_format}}-content">
<div id="footer-content" class="Box Box-condensed markdown-body pl-2">
<div id="wiki-footer" class="gollum-{{footer_format}}-content my-2">
<div id="footer-content" class="Box Box-condensed markdown-body px-4">
{{{footer_content}}}
</div>
</div>
@@ -53,9 +56,9 @@
{{/preview}}
{{/historical}}
{{#historical}}
<p>This version of the page was edited by <b>{{author}}</b> at {{date}}. <a href="{{full_url_path}}">View the most recent version.</a></p>
<p>This version of the page was edited by <b>{{author}}</b> at <time datetime="{{datetime}}" data-format="{{date_format}}">{{date}}</time>. <a href="{{full_url_path}}">View the most recent version.</a></p>
{{/historical}}
</div>
</div>
+15 -4
View File
@@ -17,18 +17,29 @@ module Precious
def authored_date
@commit.authored_date
end
def datetime
authored_date.utc.iso8601
end
def message
@commit.message
end
def files
files = @diff.split(%r{^diff --git a/.+ b/.+$}).reject(&:empty?)
files = @diff.force_encoding(Encoding::UTF_8).scan(%r{
^diff\ --git\ # diff start
.+? # diff body
(?=^diff\ --git|\Z) # scan until next diff or string
}sxmu)
files.map do |diff|
matched = diff.match(%r{(?<=^--- a/).+$})
matched = diff.match(%r{(?<=^\+\+\+ b/).+$}) if matched.nil?
match = diff.match(%r{^diff --git (")?[ab]/(.+)(?(1)") (")?[ab]/(.+)(?(3)")})
path = match[2]
path = match[4] if path.nil?
{
path: matched[0],
path: path,
lines: lines(diff)
}
end
+18 -7
View File
@@ -19,16 +19,21 @@ module Precious
def lines(diff = @diff)
lines = []
lines_to_parse = diff.split("\n")[4..-1]
# If the diff is of a rename, the diff header will be one line longer than normal because it will contain a line starting with '+++' to indicate the 'new' filename.
# Make sure to skip that header line if it is present.
lines_to_parse = lines_to_parse[1..-1] if lines_to_parse[0].start_with?('+++')
lines_to_parse = diff.split("\n")[3..-1]
lines_to_parse = lines_to_parse[2..-1] if lines_to_parse[0] =~ /^(---|rename to )/
if lines_to_parse.nil? || lines_to_parse.empty?
lines_to_parse = [] # File is created without content
else
lines_to_parse = lines_to_parse[1..-1] if lines_to_parse[0].start_with?('+++')
end
lines_to_parse.each_with_index do |line, line_index|
lines << { :line => line,
:class => line_class(line),
:ldln => left_diff_line_number(line),
:rdln => right_diff_line_number(line) }
end if diff
end
lines
end
@@ -41,6 +46,8 @@ module Precious
def line_class(line)
if line =~ /^@@/
'gc'
elsif git_line?(line)
'gg'
elsif line =~ /^\+/
'gi'
elsif line =~ /^\-/
@@ -53,7 +60,7 @@ module Precious
@left_diff_line_number = nil
def left_diff_line_number(line)
if line =~ /^@@/
if git_line?(line)
m, li = *line.match(/\-(\d+)/)
@left_diff_line_number = li.to_i
@current_line_number = @left_diff_line_number
@@ -75,7 +82,7 @@ module Precious
@right_diff_line_number = nil
def right_diff_line_number(line)
if line =~ /^@@/
if git_line?(line)
m, ri = *line.match(/\+(\d+)/)
@right_diff_line_number = ri.to_i
@current_line_number = @right_diff_line_number
@@ -93,6 +100,10 @@ module Precious
end
ret
end
def git_line?(line)
!!(line =~ /^(\\ No newline|Binary files|@@)/)
end
end
end
end
+3 -2
View File
@@ -2,6 +2,7 @@ module Precious
module Views
class Create < Layout
include Editable
include HasMath
attr_reader :page, :name
@@ -41,9 +42,9 @@ module Precious
def content
@template_page
end
private
def find_format
@found_format ||= (Gollum::Page.format_for("#{@name}#{@ext}") || default_markup)
end
+2 -5
View File
@@ -3,6 +3,7 @@ module Precious
class Edit < Layout
include Editable
include HasPage
include HasMath
attr_reader :page, :content
@@ -18,10 +19,6 @@ module Precious
def page_name
@name
end
def mathjax
@mathjax
end
def header
if @header.nil?
@@ -67,7 +64,7 @@ module Precious
def etag
@etag
end
def allow_uploads
@allow_uploads
end
+11
View File
@@ -0,0 +1,11 @@
module Precious
module HasMath
def mathjax
@mathjax
end
def mathjax_config
@mathjax_config
end
end
end
+4 -1
View File
@@ -61,7 +61,10 @@ module Precious
url.compact!
return nil if url.empty?
::File.join(*url).gsub(%r{/{2,}}, '/')
_url = ::File.join(*url)
_url.gsub!(%r{/{2,}}, '/')
_url.gsub!(%r{\?}, '%3F')
_url
end
end
@@ -0,0 +1,82 @@
module Precious
module Views
module LocaleHelpers
NO_METHOD_MESSAGE = 'Argument must be a view method'
YAML_VARIABLE_REGEXP = /\%\{[\w]+\}/
# Returns all I18n translation strings for the current view class.
# This method support YAML arguments. For example:
#
# last_edited: This content was last edited at %{date}.
#
# Where the `date` argument must be a method available on the current
# class.
#
# Use this interface within Mustache templates to render any user
# interface strings in the current locale. For example:
#
# {{ t.last_edited }}
#
def t
autofill I18n.t(locale_klass_name)
end
private
# Recursively looks up I18n translation values and autofills any YAML
# arguments with the return value of the current class's matching method.
#
# When a translation value with an argument has no matching method, we
# then return that value transformed to include the `no_method_message`
#
def autofill(yaml)
yaml.map { |i18n_key, i18n_value|
if i18n_value.is_a? Hash
[i18n_key, autofill(i18n_value)]
elsif has_arguments?(i18n_value)
fill_argument_content(i18n_key, i18n_value)
else
[i18n_key, i18n_value]
end
}.to_h
end
def fill_argument_content(i18n_key, i18n_value)
i18n_value = i18n_value.gsub(YAML_VARIABLE_REGEXP) do |argument|
method_name = argument.gsub(/[^\w]/, '')
next if method_name.nil?
begin
self.public_send(method_name)
rescue NoMethodError => error
no_method_message(method_name)
end
end
[i18n_key, i18n_value]
end
def has_arguments?(i18n_value)
i18n_value.match?(YAML_VARIABLE_REGEXP)
end
# Returns the current class name in a format that is acceptable in YAML.
# To summarize its function:
#
# NameOfConstant => name_of_constant
#
def locale_klass_name
@locale_klass_name ||= self.class.name.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr('-', '_').
downcase
end
def no_method_message(method_name, message = NO_METHOD_MESSAGE)
"[#{message}: #{method_name}]"
end
end
end
end
+17 -12
View File
@@ -1,6 +1,8 @@
module Precious
module Views
class History < Layout
DATE_FORMAT = '%B %d, %Y'
include HasPage
include Pagination
include HasUserIcons
@@ -18,18 +20,21 @@ module Precious
@versions.map do |v|
i -= 1
filename = path_for_version(v.tracked_pathname)
{ :id => v.id,
:id7 => v.id[0..6],
:href => page_route("gollum/commit/#{v.id}"),
:href_page => page_route("#{filename}/#{v.id}"),
:num => i,
:selected => @page.version.id == v.id,
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
:date => v.authored_date.strftime("%B %d, %Y"),
:user_icon => self.user_icon_code(v.author.email),
:filename => filename,
:date_full => v.authored_date,
authored_date = v.authored_date
{ :id => v.id,
:id7 => v.id[0..6],
:href => page_route("gollum/commit/#{v.id}"),
:href_page => page_route("#{filename}/#{v.id}"),
:num => i,
:selected => @page.version.id == v.id,
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
:date_full => authored_date,
:date => authored_date.strftime(DATE_FORMAT),
:datetime => authored_date.utc.iso8601,
:date_format => DATE_FORMAT,
:user_icon => self.user_icon_code(v.author.email),
:filename => filename
}
end
end
+15 -10
View File
@@ -1,6 +1,8 @@
module Precious
module Views
class LatestChanges < Layout
DATE_FORMAT = '%B %d, %Y'
include Pagination
include HasUserIcons
@@ -14,16 +16,19 @@ module Precious
i = @versions.size + 1
@versions.map do |v|
i -= 1
{ :id => v.id,
:id7 => v.id[0..6],
:href => page_route("gollum/commit/#{v.id}"),
:num => i,
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
:date => v.authored_date.strftime("%B %d, %Y"),
:user_icon => self.user_icon_code(v.author.email),
:date_full => v.authored_date,
:files => v.stats.files.map { |f|
authored_date = v.authored_date
{ :id => v.id,
:id7 => v.id[0..6],
:href => page_route("gollum/commit/#{v.id}"),
:num => i,
:author => v.author.name.respond_to?(:force_encoding) ? v.author.name.force_encoding('UTF-8') : v.author.name,
:message => v.message.respond_to?(:force_encoding) ? v.message.force_encoding('UTF-8') : v.message,
:date_full => authored_date,
:date => authored_date.strftime(DATE_FORMAT),
:datetime => authored_date.utc.iso8601,
:date_format => DATE_FORMAT,
:user_icon => self.user_icon_code(v.author.email),
:files => v.stats.files.map { |f|
new_path = extract_page_dir(f[:new_file])
{ :file => new_path,
:link => "#{page_route(new_path)}/#{v.id}",
+20 -2
View File
@@ -6,10 +6,11 @@ module Precious
include Rack::Utils
include Sprockets::Helpers
include Precious::Views::AppHelpers
include Precious::Views::LocaleHelpers
include Precious::Views::SprocketsHelpers
include Precious::Views::RouteHelpers
include Precious::Views::OcticonHelpers
alias_method :h, :escape_html
attr_reader :name, :path
@@ -47,7 +48,7 @@ module Precious
end
def mathjax_js
page_route("gollum/assets/mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML")
"#{page_route('gollum/assets/mathjax/MathJax.js')}?config=TeX-AMS-MML_HTMLorMML"
end
def css # custom css
@@ -66,6 +67,10 @@ module Precious
@per_page_uploads
end
def show_local_time
@show_local_time ? 'true' : 'false'
end
# Navigation bar
def search
false
@@ -82,6 +87,19 @@ module Precious
def latest_changes
false
end
# Passthrough additional omniauth parameters for status bar
def user_authed
@user_authed
end
def user_provider
@user.provider
end
def user_name
@user.name
end
end
end
+5 -4
View File
@@ -4,6 +4,7 @@ module Precious
module Views
class Overview < Layout
attr_reader :results, :ref, :allow_editing, :newable
HIDDEN_PATHS = ['.gitkeep']
def title
"Overview of #{@ref}"
@@ -25,9 +26,9 @@ module Precious
title = crumb.basename
if title == path.basename
breadcrumb << %{<li class="breadcrumb-item" aria-current="page">#{title}</li>}
breadcrumb << %{<li class="breadcrumb-item" aria-current="page">#{CGI.escapeHTML(title.to_s)}</li>}
else
breadcrumb << %{<li class="breadcrumb-item"><a href="#{overview_path}/#{crumb}/">#{title}</a></li>}
breadcrumb << %{<li class="breadcrumb-item"><a href="#{overview_path}/#{crumb}/">#{CGI.escapeHTML(title.to_s)}</a></li>}
end
end
breadcrumb << %{</ol></nav>}
@@ -51,9 +52,9 @@ module Precious
folder_path = @path ? "#{@path}/#{folder_name}" : folder_name
folder_url = "#{overview_path}/#{folder_path}/"
files_and_folders << {name: folder_name, icon: rocticon('file-directory'), type: 'dir', url: folder_url, is_file: false}
elsif result_path != '.gitkeep'
elsif !HIDDEN_PATHS.include?(result_path)
file_url = page_route(result.escaped_url_path)
files_and_folders << {name: result.filename, icon: rocticon('file'), type: 'file', url: file_url, is_file: true}
files_and_folders << {name: result.filename, icon: rocticon('file'), type: 'file', url: file_url, file_path: result.escaped_url_path, is_file: true}
end
end
# 1012: Overview should list folders first, followed by files and pages sorted alphabetically
+30 -22
View File
@@ -2,8 +2,9 @@ module Precious
module Views
class Page < Layout
include HasPage
include HasMath
attr_reader :content, :page, :header, :footer, :preview, :historical
attr_reader :content, :page, :header, :footer, :preview, :historical, :version
VALID_COUNTER_STYLES = ['decimal', 'decimal-leading-zero', 'arabic-indic', 'armenian', 'upper-armenian',
'lower-armenian', 'bengali', 'cambodian', 'khmer', 'cjk-decimal', 'devanagari', 'georgian', 'gujarati', 'gurmukhi',
@@ -24,7 +25,7 @@ module Precious
def page_header
title
end
def breadcrumb
path = Pathname.new(@page.url_path).parent
return '' if path.to_s == '.'
@@ -32,7 +33,7 @@ module Precious
path.descend do |crumb|
element = "#{crumb.basename}"
next if element == @page.title
breadcrumb << %{<li class="breadcrumb-item"><a href="#{overview_path}/#{crumb}/">#{element}</a></li>}
breadcrumb << %{<li class="breadcrumb-item"><a href="#{overview_path}/#{crumb}/">#{CGI.escapeHTML(element.to_s)}</a></li>}
end
breadcrumb << %{</ol></nav>}
breadcrumb.join("\n")
@@ -47,11 +48,23 @@ module Precious
return DEFAULT_AUTHOR unless first
first.author.name.respond_to?(:force_encoding) ? first.author.name.force_encoding('UTF-8') : first.author.name
end
def date_full
first = @version ? page.version : page.last_version
return Time.now unless first
first.authored_date
end
def date
first = @version ? page.version : page.last_version
return Time.now.strftime(DATE_FORMAT) unless first
first.authored_date.strftime(DATE_FORMAT)
date_full.strftime(DATE_FORMAT)
end
def datetime
date_full.utc.iso8601
end
def date_format
DATE_FORMAT
end
def noindex
@@ -61,11 +74,11 @@ module Precious
def editable
@editable
end
def search
true
end
def history
true
end
@@ -73,11 +86,11 @@ module Precious
def latest_changes
true
end
def overview
true
true
end
def allow_editing
@allow_editing
end
@@ -123,7 +136,11 @@ module Precious
def bar_side
@bar_side.to_s
end
def body_side
@bar_side == :right ? "left" : "right"
end
def left_bar
@bar_side == :left
end
@@ -153,14 +170,6 @@ module Precious
@toc_content
end
def mathjax
@mathjax
end
def mathjax_config
@mathjax_config
end
def use_identicon
@page.wiki.user_icons == 'identicon'
end
@@ -178,7 +187,7 @@ module Precious
# Returns Hash.
def metadata
@page.metadata
end
end
# Access to embedded metadata.
#
@@ -264,7 +273,6 @@ module Precious
end
result << "</tr>\n</table>\n"
end
end
end
end
+40
View File
@@ -0,0 +1,40 @@
module Precious
module Views
module TemplateCascade
def template_priority_path
@@template_priority_path
end
def template_priority_path=(path)
@@template_priority_path = File.expand_path(path)
@template = nil
end
def first_path_available(name)
default = File.join(template_path, "#{name}.#{template_extension}")
priority =
if template_priority_path
File.join(template_priority_path, "#{name}.#{template_extension}")
end
priority && File.exist?(priority) ? priority : default
end
# Method should track lib/mustache/settings.rb from Mustache project.
def template_file
@template_file || first_path_available(template_name)
end
# Method should track lib/mustache.rb from Mustache project.
def partial(name)
path = first_path_available(name)
begin
File.read(path)
rescue
raise if raise_on_context_miss?
""
end
end
end
end
end
+1
View File
@@ -10,3 +10,4 @@ cfea406f5f77afc7fb673a43e97721234385b1bd 629aa678272b017a4d136d35e77ac94d80b08dc
563cc3701db990caf63e4ce9c3697a062890ca48 874f597a5659b4c3b153674ea04e406ff393975e Charles Pence <charles@charlespence.net> 1363478075 -0400 push
874f597a5659b4c3b153674ea04e406ff393975e 7bdfe65face6f7cf9877d8c1d8c1dd974a63745e Nathan Lowe <techwiz96@gmail.com> 1421012322 -0500 push
7bdfe65face6f7cf9877d8c1d8c1dd974a63745e f803c64d11407b23797325e3843f3f378b78f611 Dawa Ometto <dawa.ometto@phil.uu.nl> 1492034760 +0200 push
f803c64d11407b23797325e3843f3f378b78f611 181c757cca395d4da18701d069a6b8123e88e040 ViChyavIn <nikita.vyach.ivanov@gmail.com> 1609841455 +0500 push
@@ -10,3 +10,4 @@ cfea406f5f77afc7fb673a43e97721234385b1bd 629aa678272b017a4d136d35e77ac94d80b08dc
563cc3701db990caf63e4ce9c3697a062890ca48 874f597a5659b4c3b153674ea04e406ff393975e Charles Pence <charles@charlespence.net> 1363478075 -0400 push
874f597a5659b4c3b153674ea04e406ff393975e 7bdfe65face6f7cf9877d8c1d8c1dd974a63745e Nathan Lowe <techwiz96@gmail.com> 1421012322 -0500 push
7bdfe65face6f7cf9877d8c1d8c1dd974a63745e f803c64d11407b23797325e3843f3f378b78f611 Dawa Ometto <dawa.ometto@phil.uu.nl> 1492034760 +0200 push
f803c64d11407b23797325e3843f3f378b78f611 181c757cca395d4da18701d069a6b8123e88e040 ViChyavIn <nikita.vyach.ivanov@gmail.com> 1609841455 +0500 push
+1 -1
View File
@@ -1 +1 @@
f803c64d11407b23797325e3843f3f378b78f611
181c757cca395d4da18701d069a6b8123e88e040
Binary file not shown.
+2
View File
@@ -3,3 +3,5 @@
f403b791119f8232b7cb0ba455c624ac6435f433 ed6c9f63b98acf73c25b5ffbb38da557d3682023 bootstraponline <cafe@bootstraponline.com> 1336421777 -0600 commit: Add header.
ed6c9f63b98acf73c25b5ffbb38da557d3682023 084a558a1fb3cded23129e2dfad3a17d07d73fd3 Daniel Kimsey <dekimsey@ufl.edu> 1354899095 -0500 push
084a558a1fb3cded23129e2dfad3a17d07d73fd3 02796b1450691f90db5d6dc6a816a4980ce80d07 Dawa Ometto <dawa.ometto@phil.uu.nl> 1538516954 +0200 push
02796b1450691f90db5d6dc6a816a4980ce80d07 db8b297cf5a31b46ac24500edfdbd0d3d8eed4eb Nikita Ivanov <nikita.vyach.ivanov@gmail.com> 1640368693 +0500 commit: Created C (markdown)
db8b297cf5a31b46ac24500edfdbd0d3d8eed4eb c736e410f98df7a1173a540364dbb8a18274273e Nikita Ivanov <nikita.vyach.ivanov@gmail.com> 1640368721 +0500 commit: Deleted C.md
@@ -3,3 +3,5 @@
f403b791119f8232b7cb0ba455c624ac6435f433 ed6c9f63b98acf73c25b5ffbb38da557d3682023 bootstraponline <cafe@bootstraponline.com> 1336421777 -0600 commit: Add header.
ed6c9f63b98acf73c25b5ffbb38da557d3682023 084a558a1fb3cded23129e2dfad3a17d07d73fd3 Daniel Kimsey <dekimsey@ufl.edu> 1354899095 -0500 push
084a558a1fb3cded23129e2dfad3a17d07d73fd3 02796b1450691f90db5d6dc6a816a4980ce80d07 Dawa Ometto <dawa.ometto@phil.uu.nl> 1538516954 +0200 push
02796b1450691f90db5d6dc6a816a4980ce80d07 db8b297cf5a31b46ac24500edfdbd0d3d8eed4eb Nikita Ivanov <nikita.vyach.ivanov@gmail.com> 1640368693 +0500 commit: Created C (markdown)
db8b297cf5a31b46ac24500edfdbd0d3d8eed4eb c736e410f98df7a1173a540364dbb8a18274273e Nikita Ivanov <nikita.vyach.ivanov@gmail.com> 1640368721 +0500 commit: Deleted C.md
@@ -0,0 +1,6 @@
x¥ŽK
Â0@]÷³ʤù4A7n¼Ã$3±AÓJ‰ooñ
.ß[<^œKÉ:‹»ºˆ€Öɸ€bY¥Á¡öI'Rh¢Ø8 hSô¨4Só¢E¦
|è†>&KZã(vÆ"
'Œ¬Ù‹°‘ÐлŽó·üÈ•àºÒ4¯p˜~Ø®Šc›òt/”ŸmœË”3¨ï;{ܲÍf·Ý*‡š‹<¥
ù-üš…Q×
+1 -1
View File
@@ -1 +1 @@
02796b1450691f90db5d6dc6a816a4980ce80d07
c736e410f98df7a1173a540364dbb8a18274273e
@@ -0,0 +1,8 @@
<nav>
<div style="background-color: red;">NAVBAR_OVERRIDE</div>
<p>Still include an original partial to ensure the fallback works even when nested from an overriden partial:</p>
<div>
{{>mobilenav}}
</div>
</nav>
@@ -0,0 +1,12 @@
<div id="wiki-wrapper" class="page">
<p>Include an overridden partial:</p>
<div id="head">
{{#navbar?}}{{>navbar}}{{/navbar?}}
</div>
<div style="background-color: red;">PAGE_OVERRIDE</div>
{{>wiki_content}}
</div>
+134
View File
@@ -0,0 +1,134 @@
require_relative "../../helper"
require_relative "../../../lib/gollum/views/helpers"
describe Precious::Views::LocaleHelpers do
class TestClass < Mustache
include Precious::Views::LocaleHelpers
def author
"J.R.R."
end
def location
"Bloemfontein"
end
end
def setup
::I18n.available_locales = [:en, :de]
::I18n.load_path = Dir[File.expand_path("test/support/locales" + "/*.yml")]
end
def teardown
I18n.locale = :en
end
let(:dummy_instance) { TestClass.new }
describe "#t" do
describe "mustache usage" do
let(:subject) { dummy_instance.render(mustache_template) }
let(:mustache_template) { "{{ t.hello_world }}" }
describe "in the default locale" do
it "returns the translation string" do
_(subject).must_equal "Hello world"
end
end
describe "in the configured locale" do
it "returns the translation string" do
I18n.locale = :de
_(subject).must_equal "Hallo Welt"
end
end
describe "translations with YAML arguments" do
let(:mustache_template) { "{{ t.author_info.full }}" }
describe "in the default locale" do
it "autofills YAML arguments" do
_(subject).must_equal "Author J.R.R. is from Bloemfontein"
end
end
describe "in the configured locale" do
it "autofills YAML arguments" do
I18n.locale = :de
_(subject).must_equal "Autor J.R.R. ist vom Bloemfontein"
end
end
end
describe "translations with invalid arguments" do
let(:mustache_template) { "{{ t.has_invalid_argument }}" }
it "fails gracefully with embedded error message" do
expected_string = "Welcome to " \
"[#{TestClass::NO_METHOD_MESSAGE}: no_matching_method]"
_(subject).must_equal expected_string
end
end
describe "out of scope translations" do
let(:mustache_template) { "{{ t.never_called }}" }
it "does not include translation keys from other classes" do
_(subject).must_be_empty
end
end
describe "missing translations" do
let(:mustache_template) { "{{ t.nested.nonexistent_key }}" }
it "outputs an empty string" do
_(subject).must_be_empty
end
end
end
describe "usage" do
let(:subject) { dummy_instance.t }
it "returns a hash" do
_(subject).must_be_kind_of Hash
end
it "returns translation keys under 'test_class'" do
i18n_keys = I18n.t("test_class").keys
_(subject.keys).must_equal i18n_keys
end
it "does not return translation keys under other classes" do
other_i18n_keys = I18n.t("nonexistant_test_class").keys
_(subject.keys).wont_include other_i18n_keys
end
it "returns nested keys" do
nested_keys = subject[:author_info].keys
_(nested_keys).must_equal [:full]
end
describe "auto-filled YAML arguments" do
let(:subject) { dummy_instance.t[:author_info][:full] }
it "auto-fills in the default locale" do
_(subject).must_equal "Author J.R.R. is from Bloemfontein"
end
it "auto-fills in a configured locale" do
I18n.locale = :de
_(subject).must_equal "Autor J.R.R. ist vom Bloemfontein"
end
end
end
end
end
+34 -14
View File
@@ -1,18 +1,20 @@
require 'rubygems'
require 'rack/test'
require 'test/unit'
require 'shoulda'
require 'minitest/autorun'
require 'minitest/reporters'
require 'minitest/spec'
require 'mocha/setup'
require 'fileutils'
require 'minitest/reporters'
require 'twitter_cldr'
require 'tmpdir'
# Silence locale validation warning
require 'i18n'
I18n.enforce_available_locales = false
MiniTest::Reporters.use!
Minitest::Reporters.use! [
Minitest::Reporters::DefaultReporter.new({color: true})
]
dir = File.dirname(File.expand_path(__FILE__))
$LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
@@ -62,14 +64,29 @@ def normal(text)
text
end
# test/spec/mini 3
# http://gist.github.com/25455
# chris@ozmm.org
# file:lib/test/spec/mini.rb
# The following configuration originates from this gist:
# http://gist.github.com/25455
#
# But it has been modified since it was first committed. It allows you to
# write tests with an RSpec-like DSL:
#
# context "my test context" do
# setup do
# # My test setup
# end
#
# teardown do
# # My test teardown
# end
#
# test "some functionality" do
# assert true
# end
# end
def context(*args, &block)
return super unless (name = args.first) && block
require 'test/unit'
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
klass = Class.new(Minitest::Test) do
def self.test(name, &block)
define_method("test_#{name.gsub(/\W/, '_')}", &block) if block
end
@@ -85,12 +102,15 @@ def context(*args, &block)
define_method(:teardown, &block)
end
end
(
class << klass;
self
end).send(:define_method, :name) { name.gsub(/\W/, '_') }
class << klass;
self
end
).send(:define_method, :name) { name.gsub(/\W/, '_') }
$contexts << klass
klass.class_eval &block
end
$contexts = []
$contexts = []
+8
View File
@@ -0,0 +1,8 @@
de:
test_class:
author_info:
full: Autor %{author} ist vom %{location}
has_invalid_argument: Willkommen in %{no_matching_method}
hello_world: Hallo Welt
nonexistant_test_class:
never_called: Nie angerufen
+8
View File
@@ -0,0 +1,8 @@
en:
test_class:
author_info:
full: Author %{author} is from %{location}
has_invalid_argument: Welcome to %{no_matching_method}
hello_world: Hello world
nonexistant_test_class:
never_called: Never called
+54 -34
View File
@@ -3,87 +3,107 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
context "Precious::Views::Editing" do
include Rack::Test::Methods
setup do
@path = cloned_testpath('examples/revert.git')
@path = cloned_testpath('examples/revert.git')
Precious::App.set(:gollum_path, @path)
Precious::App.set(:wiki_options, {allow_editing: true, allow_uploads: true})
@wiki = Gollum::Wiki.new(@path)
end
teardown do
Precious::App.set(:wiki_options, {allow_editing: true, allow_uploads: true})
FileUtils.rm_rf(@path)
end
test "creating page is blocked" do
Precious::App.set(:wiki_options, { allow_editing: false})
post "/gollum/create", :content => 'abc', :page => "D",
:format => 'markdown', :message => 'def'
assert !last_response.ok?
test 'creating pages is not blocked' do
post '/gollum/create',
content: 'abc',
format: 'markdown',
message: 'def',
page: 'D'
page = @wiki.page('D')
assert page.nil?
assert_equal last_response.status, 302
refute_nil @wiki.page('D')
end
test 'creating pages is blocked' do
Precious::App.set(:wiki_options, {allow_editing: false, allow_uploads: false})
post '/gollum/create',
content: 'abc',
format: 'markdown',
message: 'def',
page: 'D'
assert last_response.body.include? 'Forbidden. This wiki is set to no-edit mode.'
refute last_response.ok?
assert_nil @wiki.page('D')
end
test ".redirects.gollum file should not be accessible" do
Precious::App.set(:wiki_options, { allow_editing: true, allow_uploads: true })
get '/.redirects.gollum'
assert_match /Accessing this resource is not allowed/, last_response.body
end
test ".redirects.gollum file should not be editable" do
Precious::App.set(:wiki_options, { allow_editing: true, allow_uploads: true })
get '/gollum/edit/.redirects.gollum'
assert_match /Changing this resource is not allowed/, last_response.body
end
test "frontend links for editing are not blocked" do
Precious::App.set(:wiki_options, { allow_editing: true, allow_uploads: true })
get '/A'
assert_match /Delete this Page/, last_response.body, "'Delete this Page' link is blocked in page template"
assert_match /New/, last_response.body, "'New' button is blocked in page template"
assert_match /Upload\b/, last_response.body, "'Upload' link is blocked in page template"
assert_match /Rename/, last_response.body, "'Rename' link is blocked in page template"
assert_match /Edit/, last_response.body, "'Edit' link is blocked in page template"
assert last_response.body.include? "Delete this Page"
assert last_response.body.include? "New"
assert last_response.body.include? "<span>Upload</span>"
assert last_response.body.include? "Rename"
assert last_response.body.include? "Edit"
get '/gollum/overview'
assert_match /New/, last_response.body, "'New' link is blocked in pages template"
assert last_response.body.include? "New"
get '/gollum/history/A'
assert_no_match /Edit/, last_response.body, "'Edit' link is not blocked in history template"
refute last_response.body.include? "Edit"
get '/gollum/compare/A/fc66539528eb96f21b2bbdbf557788fe8a1196ac..b26b791cb7917c4f37dd9cb4d1e0efb24ac4d26f'
get '/gollum/compare/A/fc665395..b26b791c'
assert_no_match /Edit Page/, last_response.body, "'Edit Page' link is not blocked in compare template"
assert_match /Revert Changes/, last_response.body, "'Revert Changes' link is blocked in compare template"
refute last_response.body.include? "Edit Page"
assert last_response.body.include? "Revert Changes"
end
test "frontend links for editing blocked" do
Precious::App.set(:wiki_options, { allow_editing: false })
Precious::App.set(:wiki_options, {allow_editing: false, allow_uploads: false})
get '/A'
assert_no_match /Delete this Page/, last_response.body, "'Delete this Page' link not blocked in page template"
assert_no_match /New/, last_response.body, "'New' button not blocked in page template"
assert_no_match /Upload\b/, last_response.body, "'Upload' link not blocked in page template"
assert_no_match /Rename/, last_response.body, "'Rename' link not blocked in page template"
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in page template"
refute last_response.body.include? "Delete this Page"
refute last_response.body.include? "<span>Upload</span>"
refute last_response.body.include? "Rename"
refute last_response.body.include? "Edit"
refute last_response.body.include? "New"
get '/gollum/overview'
assert_no_match /New/, last_response.body, "'New' link not blocked in pages template"
refute last_response.body.include? "New"
get '/gollum/history/A'
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in history template"
refute last_response.body.include? "Edit"
get '/gollum/compare/A/fc66539528eb96f21b2bbdbf557788fe8a1196ac..b26b791cb7917c4f37dd9cb4d1e0efb24ac4d26f'
get '/gollum/compare/A/fc665395..b26b791c'
assert_no_match /Edit Page/, last_response.body, "'Edit Page' link not blocked in compare template"
assert_no_match /Revert Changes/, last_response.body, "'Revert Changes' link not blocked in compare template"
refute last_response.body.include? "Edit Page"
refute last_response.body.include? "Revert Changes"
end
def app
Precious::App
end
end
end
+157 -76
View File
@@ -14,7 +14,7 @@ context "Frontend" do
teardown do
FileUtils.rm_rf(@path)
end
test "utf-8 kcode" do
assert_equal 'μ†ℱ'.scan(/./), ["μ", "", ""]
end
@@ -39,31 +39,29 @@ context "Frontend" do
assert_match /<pre><code>one\ntwo\nthree\nfour\n<\/code><\/pre>\n/m, last_response.body
end
def nfd utf8
TwitterCldr::Normalization.normalize(utf8, using: :nfd)
end
test 'mathjax assets are served' do
get '/gollum/assets/mathjax/MathJax.js'
assert last_response.ok?
end
test "UTF-8 headers href preserved" do
page = 'utfh1'
text = nfd('한글')
page_content = <<~TEXT
## 한글
# don't use h1 or it will be promoted to replace file name
# which doesn't generate a normal header link
@wiki.write_page(page, :markdown, '## ' + text,
{ :name => 'user1', :email => 'user1' });
Test page "utfh1" content.
TEXT
get page
expected = "<h2 class=\"editable\"><a class=\"anchor\" (href|id)=\"(#)?#{text}\" (href|id)=\"(#)?#{text}\"></a>#{text}</h2>"
actual = nfd(last_response.body)
@wiki.write_page('utfh1',
:markdown,
page_content,
{name: 'user1', email: 'user1'})
assert_match /#{expected}/, actual
get 'utfh1'
expected = "<h2 class=\"editable\"><a class=\"anchor\" (href|id)=\"(#)?한글\" (href|id)=\"(#)?한글\"></a>한글</h2>"
assert_match /#{expected}/, last_response.body
end
test 'rss feed' do
channel_title = <<EOF
<title>Gollum Wiki Latest Changes</title>
@@ -106,24 +104,24 @@ EOF
page_2 = @wiki.page(page_1.name)
assert_equal 'abc', page_2.raw_data
assert_equal 'def', page_2.version.message
assert_not_equal page_1.version.sha, page_2.version.sha
refute_equal page_1.version.sha, page_2.version.sha
end
test "edit page fails when page is outdated (edit collision)" do
page = @wiki.page('A')
old_sha = page.sha
post "/gollum/edit/A", :content => 'abc', :page => 'A',
:format => page.format, :message => 'def', :etag => old_sha
assert last_response.ok?
@wiki.clear_cache
page = @wiki.page('A')
new_sha = page.sha
assert_not_equal old_sha, new_sha
refute_equal old_sha, new_sha
post "/gollum/edit/A", :content => 'def', :page => 'A',
:format => page.format, :message => 'def', :etag => old_sha
assert_equal last_response.status, 412
assert_equal last_response.status, 412
end
test "edit page with empty message" do
@@ -136,7 +134,7 @@ EOF
page_2 = @wiki.page(page_1.name)
assert_equal 'abc', page_2.raw_data
assert_equal '[no message]', page_2.version.message
assert_not_equal page_1.version.sha, page_2.version.sha
refute_equal page_1.version.sha, page_2.version.sha
end
test "edit page with slash" do
@@ -167,13 +165,13 @@ EOF
assert_equal 'header', header_2.raw_data
assert_equal 'footer', foot_2.raw_data
assert_equal 'def', foot_2.version.message
assert_not_equal foot_1.version.sha, foot_2.version.sha
assert_not_equal header_1.version.sha, header_2.version.sha
refute_equal foot_1.version.sha, foot_2.version.sha
refute_equal header_1.version.sha, header_2.version.sha
assert_equal 'sidebar', side_2.raw_data
assert_equal 'def', side_2.version.message
assert_not_equal side_1.version.sha, side_2.version.sha
assert_equal commits+1, @wiki.repo.commits('master').size
refute_equal side_1.version.sha, side_2.version.sha
assert_equal commits, @wiki.repo.commits('master').size
end
test "renames page" do
@@ -189,7 +187,7 @@ EOF
page_2 = @wiki.page('C')
assert_equal "INITIAL\n\nSPAM2\n", page_2.raw_data
assert_equal 'def', page_2.last_version.message
assert_not_equal page_1.version.sha, page_2.version.sha
refute_equal page_1.version.sha, page_2.version.sha
end
test "rename preserves format" do
@@ -224,7 +222,7 @@ EOF
test "renames page in subdirectory" do
page_1 = @wiki.page("G/H")
assert_not_equal page_1, nil
refute_equal page_1, nil
post "/gollum/rename/G/H", :rename => "/I/C", :message => 'def'
follow_redirect!
@@ -236,12 +234,12 @@ EOF
page_2 = @wiki.page('I/C')
assert_equal "INITIAL\n\nSPAM2\n", page_2.raw_data
assert_equal 'def', page_2.last_version.message
assert_not_equal page_1.version.sha, page_2.version.sha
refute_equal page_1.version.sha, page_2.version.sha
end
test "renames page relative in subdirectory" do
page_1 = @wiki.page("G/H")
assert_not_equal page_1, nil
refute_equal page_1, nil
post "/gollum/rename/G/H", :rename => "K/C", :message => 'def'
follow_redirect!
@@ -253,7 +251,7 @@ EOF
page_2 = @wiki.page('G/K/C')
assert_equal "INITIAL\n\nSPAM2\n", page_2.raw_data
assert_equal 'def', page_2.last_version.message
assert_not_equal page_1.version.sha, page_2.version.sha
refute_equal page_1.version.sha, page_2.version.sha
end
test "creates page" do
@@ -322,17 +320,17 @@ EOF
name = "#{dir}/bar"
get "/gollum/create/#{name}"
assert_match(/\/#{dir}/, last_response.body)
assert_no_match(/[^\/]#{dir}/, last_response.body)
refute_match(/[^\/]#{dir}/, last_response.body)
end
test "create with template succeed if template exists" do
Precious::App.set(:wiki_options, { :template_page => true })
page='_Template'
post '/gollum/create', :content => 'fake template', :page => page,
post '/gollum/create', :content => 'fake template with some Utf-8: Ü', :page => page,
:path => '/', :format => 'markdown', :message => ''
follow_redirect!
follow_redirect!
assert last_response.ok?
@wiki.clear_cache
@wiki.clear_cache
get "/gollum/create/TT"
assert last_response.ok?
post '/gollum/delete/_Template'
@@ -340,12 +338,51 @@ EOF
end
test "create with template succeed if template doesn't exist" do
Precious::App.set(:wiki_options, { :template_page => true })
Precious::App.set(:wiki_options, { :template_page => true })
get "/gollum/create/TT"
assert last_response.ok?
Precious::App.set(:wiki_options, { :template_page => false })
end
test "create with template filter without parameter" do
Precious::App.set(:wiki_options, { :template_page => true })
# arrange
now = Time.parse('2022-04-16')
Gollum::TemplateFilter.add_filter("{{today}}", & -> () { now.strftime("%Y-%m-%d") })
template_content = "# Daily Log, {{today}}"
@wiki.write_page("daily-logs/_Template",
:markdown,
template_content)
# act
get "/gollum/create/daily-logs/test"
# assert
assert last_response.ok?
assert_match("# Daily Log, 2022-04-16", last_response.body)
Precious::App.set(:wiki_options, { :template_page => false })
end
test "create with template filter with parameter" do
Precious::App.set(:wiki_options, { :template_page => true })
# arrange
Gollum::TemplateFilter.add_filter("{{page_name}}", & -> (page) { page.name })
template_content = "# Daily Log, {{page_name}}"
@wiki.write_page("daily-logs/_Template",
:markdown,
template_content)
# act
get "/gollum/create/daily-logs/2022-04-16"
# assert
assert last_response.ok?
assert_match("# Daily Log, 2022-04-16", last_response.body)
Precious::App.set(:wiki_options, { :template_page => false })
end
test "edit returns nil for non-existant page" do
# post '/edit' fails. post '/edit/' works.
page = 'not-real-page'
@@ -353,7 +390,7 @@ EOF
post '/gollum/edit/', :content => 'edit_msg',
:page => page, :path => path, :message => ''
page_e = @wiki.page(::File.join(path,page))
assert_equal nil, page_e
assert_nil page_e
end
test "edit allows changing format" do
@@ -409,36 +446,72 @@ EOF
@wiki.clear_cache
page = @wiki.page(name)
assert_not_equal 'abc', page.raw_data
refute_equal 'abc', page.raw_data
end
test "uploading is not allowed unless explicitly enabled" do
temp_upload_file = Tempfile.new(['upload', '.file']) << 'abc'
temp_upload_file.close
post "/gollum/upload_file", :file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))
Precious::App.set(
:wiki_options,
{allow_uploads: false, per_page_uploads: false}
)
post '/gollum/upload_file',
file: Rack::Test::UploadedFile.new(File.open(temp_upload_file))
assert_equal 405, last_response.status
end
test "upload a file with mode dir" do
temp_upload_file = Tempfile.new(['upload', '.file']) << 'abc'
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true})
post "/gollum/upload_file", :file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
file = @wiki.file("uploads/#{::File.basename(temp_upload_file.path)}")
assert_equal 'abc', file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false})
end
test "upload a file with mode page" do
temp_upload_file = Tempfile.new(['upload', '.file']) << 'abc'
temp_upload_file = Tempfile.new(['upload', '.file']) << "abc\r"
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true, per_page_uploads: true})
post "/gollum/upload_file", {:file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))}, {'HTTP_REFERER' => 'http://localhost:4567/Home.md', 'HTTP_HOST' => 'localhost:4567'}
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
# Find the file in a page-specific subdir (here: Home), based on referer
file = @wiki.file("uploads/Home/#{::File.basename(temp_upload_file.path)}")
assert_equal "abc\r", file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "upload a file with valid extension" do
temp_upload_file = Tempfile.new(['upload', '.txt']) << "abc\r"
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true, per_page_uploads: true})
post "/gollum/upload_file", {:file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))}, {'HTTP_REFERER' => 'http://localhost:4567/Home.md', 'HTTP_HOST' => 'localhost:4567'}
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
# Find the file in a page-specific subdir (here: Home), based on referer
file = @wiki.file("uploads/Home/#{::File.basename(temp_upload_file.path)}")
assert_equal "abc", file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "upload a file with https referer" do
temp_upload_file = Tempfile.new(['https_upload', '.file']) << 'abc'
temp_upload_file.close
Precious::App.set(:wiki_options, {allow_uploads: true, per_page_uploads: true})
post "/gollum/upload_file", {:file => Rack::Test::UploadedFile.new(::File.open(temp_upload_file))}, {'HTTP_REFERER' => 'https://localhost:4567/Home.md', 'HTTP_HOST' => 'localhost:4567'}
assert_equal 302, last_response.status # redirect is expected
@wiki.clear_cache
# Find the file in a page-specific subdir (here: Home), based on referer
@@ -446,7 +519,8 @@ EOF
assert_equal 'abc', file.raw_data
Precious::App.set(:wiki_options, {allow_uploads: false, per_page_uploads: false})
end
test "guard against uploading an existing file" do
temp_upload_file = Tempfile.new(['upload', '.file']) << 'abc'
temp_upload_file.close
@@ -458,7 +532,7 @@ EOF
assert_equal 409, last_response.status
Precious::App.set(:wiki_options, {allow_uploads: false})
end
test "delete a page" do
name = "deleteme"
post "/gollum/create", :content => 'abc', :page => name,
@@ -470,15 +544,15 @@ EOF
@wiki.clear_cache
page = @wiki.page(name)
assert_equal nil, page
assert_nil page
end
test "previews content" do
post "/gollum/preview", :content => 'abc', :format => 'markdown', :page => 'Samewise%20Gamgee.mediawiki'
post "/gollum/preview", :content => 'abc', :format => 'markdown', :page => 'Samewise Gamgee.mediawiki'
assert last_response.ok?
assert last_response.body.include?('Samewise Gamgee</h1>')
assert last_response.body.include?('Samewise Gamgee')
end
test 'throws an error when comparing two identical revisions for a page' do
get '/gollum/compare/A.md/fc66539528eb96f21b2bbdbf557788fe8a1196ac...fc66539528eb96f21b2bbdbf557788fe8a1196ac'
assert last_response.ok?
@@ -494,7 +568,7 @@ EOF
@wiki.clear_cache
page2 = @wiki.page('B')
assert_not_equal page1.version.sha, page2.version.sha
refute_equal page1.version.sha, page2.version.sha
assert_equal "INITIAL", page2.raw_data.strip
assert_equal "Revert commit 7c45b5f", page2.version.message
end
@@ -508,7 +582,7 @@ EOF
@wiki.clear_cache
page2 = @wiki.page('A')
assert_not_equal page1.version.sha, page2.version.sha
refute_equal page1.version.sha, page2.version.sha
assert_equal "INITIAL", page2.raw_data.strip
end
@@ -522,7 +596,7 @@ EOF
page2 = @wiki.page('A')
assert_equal page1.version.sha, page2.version.sha
end
=begin
# redirects are now handled by class MapGollum in bin/gollum
# they should be set in config.ru
@@ -570,7 +644,7 @@ EOF
{ :name => 'user1', :email => 'user1' });
get page
assert_no_match /custom.js/, last_response.body
refute_match /custom.js/, last_response.body
end
test "add custom.js if setting" do
@@ -588,7 +662,7 @@ EOF
test "don't allow changing custom js or css" do
Precious::App.set(:wiki_options, { :js => true, :css => true })
['create', 'edit'].each do |route|
['.css', '.js'].each do |ext|
get "/gollum/#{route}/custom#{ext}"
@@ -621,7 +695,7 @@ EOF
:page => 'Multibyte', :format => :markdown, :message => 'mesg'
page = @wiki.page('Multibyte')
post "/gollum/edit/Multibyte",
:content => 'りんご', :header => 'みかん', :footer => 'バナナ', :sidebar => 'スイカ',
:page => 'Multibyte', :format => :markdown, :message => 'mesg', :etag => page.sha
@@ -639,7 +713,7 @@ EOF
get "A"
assert last_response.ok?
assert_no_match /meta name="robots" content="noindex, nofollow"/, last_response.body
refute_match /meta name="robots" content="noindex, nofollow"/, last_response.body
get "A/fc66539528eb96f21b2bbdbf557788fe8a1196ac"
@@ -655,6 +729,13 @@ EOF
assert_equal last_response.status, 302
end
test "view deleted page in history" do
get 'C/db8b297cf5a31b46ac24500edfdbd0d3d8eed4eb'
assert last_response.ok?
assert_match /This page will be deleted in the next commit :\(/, last_response.body
end
def app
Precious::App
end
@@ -804,17 +885,17 @@ context "Frontend with lotr" do
assert last_response.ok?
assert_equal last_response.body.include?('delete-link'), false
assert_equal last_response.body.include?('page-info-toggle'), false
assert last_response.body.include?('This version of the page was edited by <b>Tom Preston-Werner</b> at 2010-04-07')
assert_match %r{This version of the page was edited by <b>Tom Preston-Werner</b> at <time datetime="2010-04-07T19:49:43Z" data-format="%Y-%m-%d %H:%M:%S">\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}</time>.}, last_response.body
assert last_response.body.include?("<a href=\"/Bilbo-Baggins.md\">View the most recent version.</a></p>")
end
test "show revision of specific file" do
old_sha = "df26e61e707116f81ebc6b935ec6d1676b7e96c4"
update_sha = "f803c64d11407b23797325e3843f3f378b78f611"
get "Data.csv/#{old_sha}"
assert last_response.ok?
assert_no_match /Samwise,Gamgee/, last_response.body
refute_match /Samwise,Gamgee/, last_response.body
get "Data.csv/#{update_sha}"
assert last_response.ok?
@@ -859,7 +940,7 @@ context "Frontend with page-file-dir" do
name = "#{dir}/baz"
get "/gollum/create/#{name}"
assert_match(/\/#{dir}/, last_response.body)
assert_no_match(/[^\/]#{dir}/, last_response.body)
refute_match(/[^\/]#{dir}/, last_response.body)
end
test "use custom.css from page-file-dir path if page-file-dir is set" do
@@ -900,7 +981,7 @@ end
context "Frontend with empty repo" do
include Rack::Test::Methods
setup do
@path = cloned_testpath("examples/empty.git")
@wiki = Gollum::Wiki.new(@path)
@@ -911,11 +992,11 @@ context "Frontend with empty repo" do
teardown do
FileUtils.rm_rf(@path)
end
def app
Precious::App
end
test 'previews content on the first page of an empty wiki' do
post '/gollum/preview', :content => 'abc', :format => 'markdown'
assert last_response.ok?
@@ -927,12 +1008,12 @@ context "Frontend with empty repo" do
assert_equal '/gollum/create/Home', last_request.fullpath
assert last_response.ok?
end
end
context 'Frontend with base path' do
include Rack::Test::Methods
setup do
@path = cloned_testpath("examples/lotr.git")
@wiki = Gollum::Wiki.new(@path)
@@ -944,24 +1025,24 @@ context 'Frontend with base path' do
teardown do
FileUtils.rm_rf(@path)
end
test 'page with base path' do
get '/wiki/Home'
assert last_response.ok?
end
test 'base path mathjax assets' do
get '/wiki/Home'
assert last_response.ok?
assert last_response.body.include?('<script defer src="/wiki/gollum/assets/mathjax/MathJax.js')
assert last_response.body.include?('<script defer src="/wiki/gollum/assets/mathjax/MathJax.js?config=')
end
test 'compare view' do
get '/wiki/gollum/compare/Bilbo-Baggins.md?versions[]=f25eccd98e9b667f9e22946f3e2f945378b8a72d&versions[]=5bc1aaec6149e854078f1d0f8b71933bbc6c2e43'
follow_redirect!
assert last_response.ok?
assert_equal '/wiki/gollum/compare/Bilbo-Baggins.md/5bc1aaec6149e854078f1d0f8b71933bbc6c2e43...f25eccd98e9b667f9e22946f3e2f945378b8a72d', last_request.fullpath
get '/wiki/gollum/compare/Bilbo-Baggins.md?versions[]=f25eccd98e9b667f9e22946f3e2f945378b8a72d'
follow_redirect!
assert last_response.ok?
@@ -972,8 +1053,8 @@ context 'Frontend with base path' do
assert last_response.ok?
assert_equal '/wiki/gollum/history/Bilbo-Baggins.md', last_request.fullpath
end
def app
Precious::MapGollum.new(@base_path)
end
end
end
+59
View File
@@ -0,0 +1,59 @@
# ~*~ encoding: utf-8 ~*~
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
require File.expand_path '../../lib/gollum/views/commit', __FILE__
def get_commit_diff(sha1)
commit = @wiki.repo.commit(sha1)
@wiki.repo.diff(commit.parent.id, sha1)
end
context "Precious::Views::Compare" do
setup do
@path = cloned_testpath('examples/lotr.git')
# Precious::App.set(:gollum_path, @path)
@wiki = Gollum::Wiki.new(@path)
end
test 'rename diff' do
# JGit returns differenly formatted diffs for rename commits. Support both kinds of diff.
jgit_diff = "diff --git a/Foo.md b/Bar.md\nsimilarity index 100%\nrename from Foo.md\nrename to Bar.md"
rugged_diff = "diff --git a/Bar.md b/Bar.md\nnew file mode 100644\nindex 0000000..e69de29\n--- /dev/null\n+++ b/Bar.md\n"
[jgit_diff, rugged_diff].each do |diff|
view = Precious::Views::Compare.new
view.instance_variable_set(:@diff, diff)
assert_equal [], view.lines
end
end
test 'file addition diff' do
view = Precious::Views::Compare.new
diff = get_commit_diff 'fbabba862dfa7ac35b39042dd4ad780c9f67b8cb'
view.instance_variable_set(:@diff, diff)
assert_equal [
{:line=>"@@ -0,0 +1 @@", :class=>"gc", :ldln=>"...", :rdln=>"..."},
{:line=>"+# Eye Of Sauron", :class=>"gi", :ldln=>" ", :rdln=>"1"}
], view.lines
end
test 'empty file addition diff' do
view = Precious::Views::Compare.new
diff = get_commit_diff '181c757cca395d4da18701d069a6b8123e88e040'
view.instance_variable_set(:@diff, diff)
assert_equal [], view.lines
end
test 'binary file addition diff' do
view = Precious::Views::Compare.new
diff = get_commit_diff 'afe2034d400ba21e13361f38f74900c51dbc7fde'
view.instance_variable_set(:@diff, diff)
lines = view.lines
line = lines[0].delete :line
assert_match %r{Binary files (/dev/null and b/Mordor/eye.jpg )?differ}, line
assert_equal [{:class=>"gg", :ldln=>"...", :rdln=>"..."}], lines
end
end
+4 -2
View File
@@ -35,9 +35,11 @@ context 'Precious::Views::LatestChanges' do
get(@url)
body = last_response.body
commits_list_elements = body.scan(%r{<li class="Box-row Box-row--hover-gray border-top d-flex flex-items-center">})
assert !commits_list_elements.nil?, "the commits should be listed with this tag"
assert commits_list_elements.length == 10, "/latest_changes should include the :pagination_count commit"
assert body.include?("Charles Pence</span>"), "/latest_changes should include Author Charles Pence"
assert body.include?('1db89eb'), "/latest_changes should include the :pagination_count commit"
assert !body.include?('a8ad3c0'), "/latest_changes should not include more than :pagination_count commits"
assert body.include?('<a href="/Data-Two.csv/874f597a5659b4c3b153674ea04e406ff393975e">Data-Two.csv</a>'), "/latest_changes include links to modified files in #{body}"
assert body.include?('<a href="/Hobbit.md/874f597a5659b4c3b153674ea04e406ff393975e">Hobbit.md</a>'), "/latest_changes should include links to modified pages in #{body}"
end

Some files were not shown because too many files have changed in this diff Show More