Compare commits

...

90 Commits

Author SHA1 Message Date
Vicent Marti a7a2479f85 Release 2.2.1 2012-09-02 22:43:50 +02:00
Vicent Marti a8b230a490 Properly escape TeX data. 2012-09-02 22:27:04 +02:00
bootstraponline 041b01f171 Fix #490 2012-09-01 12:33:08 -06:00
Vicent Marti 1c475f3215 Release 2.2.0 2012-09-01 12:24:17 +02:00
Vicent Marti ab699d94b0 Do not render LaTeX locally
Offload the rendering service to MathTran.org, the free and open-source
LaTeX rendering engine.
2012-09-01 12:23:02 +02:00
bootstraponline 942d32c9b6 Fix uninstall command 2012-08-30 21:36:11 -06:00
bootstraponline 97f15f0b18 Add edit hotkey. #496 2012-08-30 21:07:57 -06:00
bootstraponline ed49358c50 Add h1-3 shortcuts. #496 2012-08-30 20:54:20 -06:00
bootstraponline 85eeecd140 Add save hotkey. 2012-08-30 20:48:44 -06:00
bootstraponline 30fd40fbe5 Fix #498
Ruby 1.8.7 and ri can't deal with <!-- appearing in documentation.
Please upgrade to the latest Ruby if you're able to.
2012-08-30 20:29:53 -06:00
bootstraponline 2ed262cacd v2.1.9 2012-08-30 20:12:28 -06:00
bootstraponline 29a1ef8f8a Skip doc on install 2012-08-30 20:04:20 -06:00
bootstraponline 772d18ee62 Fix uninstall command 2012-08-30 20:00:54 -06:00
bootstraponline dd604d9942 Fix #498 2012-08-30 19:57:20 -06:00
bootstraponline 85abc83427 v2.1.8 2012-08-30 19:41:54 -06:00
bootstraponline 6d8220629c Fix #500 2012-08-30 19:37:57 -06:00
bootstraponline 6ff7ada096 Fix #495 2012-08-30 18:31:35 -03:00
bootstraponline 8575049de5 Update get /data
Remove leading slash from page
Fix edge case with path set to '/'
2012-08-30 11:40:07 -03:00
bootstraponline bb6fb0c253 pathName is undefined when not found instead of 0 2012-08-30 11:18:02 -03:00
bootstraponline eb2ad9f840 Fix path
.key now returns undefined on failure so path can be 0.
If path is undefined then use an empty string.
2012-08-30 11:14:24 -03:00
bootstraponline d0dd23fc11 Fix path 2012-08-30 11:09:44 -03:00
bootstraponline 7ae4acbdb0 Fix #497 #492
Data url now includes path.
2012-08-30 11:03:11 -03:00
bootstraponline 881590ab37 Merge pull request #494 from releu/fix-requiring-uri-encoding-components
Move require "uri_encode_component"
2012-08-28 18:33:18 -07:00
Jan Bernacki 5e479dc5d9 move require 2012-08-28 22:44:09 +04:00
bootstraponline 7db9c2e762 Merge pull request #487 from jm/master
Fix bug with missing variable 'ext'
2012-08-27 11:08:19 -07:00
bootstraponline ce6b0ac095 v2.1.7 2012-08-25 19:21:13 -06:00
bootstraponline 420bb06988 Fix create test. 2012-08-25 18:58:20 -06:00
bootstraponline 20566f8acf Fix #484 2012-08-25 18:54:07 -06:00
bootstraponline 1d5f69704a Clean paths so they start with one slash. 2012-08-25 18:41:13 -06:00
bootstraponline 0da664299e Fix edit. 2012-08-24 13:47:17 -06:00
bootstraponline 6e8fb2b457 Disable exact on edit for now. 2012-08-24 13:38:34 -06:00
bootstraponline d1c72a4ff3 Fix #483 and #481
Exact matching of requested pages

- /page is no longer the same as /a/page
- Deleting /page only deletes /page (before it would delete /a/page instead of /page)
- Edit currently breaks the unit tests if exact matching is enabled
- Fix redirect on create
- Add @giga's checked_dir = '' fix https://github.com/giga/gollum/commit/936958b47324a09c683cb90a2560484b47e09529
- Fix create unit test
2012-08-24 13:35:37 -06:00
bootstraponline 0bf05392e4 Redirect to correct path. #481 2012-08-24 12:26:59 -06:00
bootstraponline 7ecef0c045 Fix #479 2012-08-23 14:11:28 -06:00
bootstraponline 33ca329253 Handle nil slash. 2012-08-23 12:30:08 -06:00
bootstraponline 01fa4770cb Fix page lookup. #473 2012-08-23 12:16:40 -06:00
bootstraponline b76257c49c Restore foward slash. 2012-08-23 11:59:56 -06:00
bootstraponline e2fbf22f38 Fix #473. 2012-08-23 11:51:20 -06:00
bootstraponline 134432d029 v2.1.6 2012-08-23 11:33:00 -06:00
bootstraponline 8d4d6e80b8 Fix #475. 2012-08-23 11:27:40 -06:00
bootstraponline 85e6ef3dca v2.1.5 2012-08-22 19:57:22 -06:00
bootstraponline 60f1467229 Fix encoding for 1.8. 2012-08-22 18:51:41 -06:00
bootstraponline 1757242382 Don't unpack Fixnum. 2012-08-22 17:40:53 -06:00
bootstraponline 55df7bb9c4 Use encodeURIComponent instead of CGI::escape when output is sent to browser. 2012-08-22 17:30:46 -06:00
bootstraponline 686b8acd38 Fix encodeURIComponent on Ruby 1.8.
Replace .ord with .unpack('U')[0]
2012-08-22 17:23:08 -06:00
bootstraponline a5f9df6170 Restore CGI::escape. 2012-08-22 16:57:24 -06:00
bootstraponline 27f61a870a Merge pull request #477 from LuminosoInsight/master
Fix titles created with the "New" button.
2012-08-22 15:54:23 -07:00
bootstraponline a48e8d1c5c New editor style.
Disable MathJax in live preview. Parser isn't MathJax aware.
Performance of MathJax in live preview is not great.
Auto hide editor overflow.
2012-08-22 16:50:57 -06:00
bootstraponline b80f74bccd Remove protocol and host. 2012-08-22 13:32:18 -06:00
bootstraponline 1e768734ef Disable MathJax by default.
GitHub.com doesn't support MathJax. The parser is not MathJax aware which causes problems.

--mathjax enables MathJax.
2012-08-22 13:31:21 -06:00
bootstraponline 11c9cabeb3 Fix edit baseUrl. 2012-08-22 13:21:01 -06:00
bootstraponline 3a14ab92f0 Fix new page when using map '/wiki' in config.ru. 2012-08-22 13:15:18 -06:00
bootstraponline 62d5f52398 Fix anchor for Firefox. 2012-08-22 13:10:11 -06:00
Rob Speer 2b4848566c Fix names created with the 'New' button.
Previously, a page created with the 'New' button would get all of its
spaces turned into the + symbol when submitted, which Gollum would then
convert into "-plus-" in the title. So, for example, a request to create
a page called "Test page" would instead get "test plus page".

This change changes + to - in the parameter received by /create/.
2012-08-22 14:15:20 -04:00
bootstraponline 7c825e877c Remove protocol + host. 2012-08-20 15:51:07 -06:00
bootstraponline 8417c277e6 Fix delete link when using baseUrl. 2012-08-20 15:23:07 -06:00
bootstraponline 9cef423908 Remove default value in new page. 2012-08-19 14:25:02 -06:00
bootstraponline e73c84490e Remove prefix. #470. 2012-08-16 12:01:03 -06:00
bootstraponline 6cfc807db0 Revert still broken in Grit. 2012-08-15 12:22:03 -06:00
bootstraponline be366f8103 v2.1.4 2012-08-15 12:06:12 -06:00
bootstraponline 2d13bd796f Improve #470 fix. 2012-08-15 12:03:48 -06:00
bootstraponline 523f8f80ca Fix #470. 2012-08-15 11:48:17 -06:00
bootstraponline b76fef9143 Update version to 2.1.3 2012-08-13 11:37:56 -06:00
bootstraponline e935af83d5 Update gemspec. 2012-08-13 11:35:15 -06:00
bootstraponline d4e019ef42 Merge pull request #331 from tjh/home-page-link-issue
Redirect from root to /Home, fixes #250
2012-08-09 10:41:31 -07:00
bootstraponline 2e738828c6 Merge pull request #467 from jamesduncombe/pygments_initialize
Added Pygments.start to initialize Pygments on startup, fixes #465
2012-08-09 10:21:59 -07:00
James Duncombe fd7dc93778 Added Pygments.start to initialize Pygments on startup 2012-08-09 09:14:55 +01:00
bootstraponline 3df407d9ee Merge pull request #464 from giga/master
Fix editor when base url is '/'
2012-08-08 10:53:45 -07:00
Jean-Philippe Garcia Ballester 6fbba84725 Revert "Base path is relative."
This reverts commit 69e453ea0b.
2012-08-08 13:49:11 +02:00
Jean-Philippe Garcia Ballester 37d20fa9cc Base url without trailing '/'
This will allow cleaner url in templates and javascripts.
2012-08-08 13:49:09 +02:00
bootstraponline 410cd912ac Fix releasing. 2012-08-06 18:31:36 -06:00
bootstraponline d234bbd861 Test get %r{/(.+?)/([0-9a-f]{40})} do 2012-08-06 18:27:12 -06:00
Corey Donohoe 3f68d96815 setup proper gem version numbers 2012-08-06 16:32:16 -07:00
bootstraponline 015cd895d2 Don't ignore version. 2012-08-06 17:18:30 -06:00
bootstraponline 5fa4b48d85 Fix new page default dir. 2012-08-06 15:57:46 -06:00
bootstraponline 88a3783bbc Remove wpage. 2012-08-06 15:55:05 -06:00
bootstraponline ea2254b9bd Fix #462.
Add test for 462.
2012-08-06 15:52:19 -06:00
bootstraponline 9a0c1f2605 Fix path. 2012-08-06 15:18:15 -06:00
bootstraponline 008f26bb7d Fix app. 2012-08-06 14:49:56 -06:00
bootstraponline 69e453ea0b Base path is relative. 2012-08-06 14:28:24 -06:00
bootstraponline 88b0b608f4 Add null guard. 2012-08-06 13:49:48 -06:00
bootstraponline 19e3987ae3 Fix path escaping. 2012-08-06 13:36:28 -06:00
bootstraponline c72e91ddaf Fix #462. 2012-08-06 13:16:31 -06:00
bootstraponline c789dd5067 Update org-ruby. 2012-08-02 19:45:06 -06:00
bootstraponline 551949de29 Fix #459. 2012-08-02 16:55:24 -06:00
bootstraponline 80730ee87a Merge pull request #419 from trans/metadata
Add support for embedded page metadata.
2012-08-01 16:05:10 -07:00
trans 71028adc9e Add support for embedded page metadata. 2012-08-01 18:35:53 -04:00
bootstraponline a2efebc06c Fix #453. 2012-08-01 13:04:04 -06:00
Jeremy McAnally 3767a11d21 Rename variables to be more clear and fix reference to non-existent 'ext' variable 2012-07-04 12:33:09 -04:00
Tim Harvey 43d2143506 Redirect from root to /Home, fixes #250 2012-05-10 08:55:56 -04:00
34 changed files with 461 additions and 526 deletions
+10 -4
View File
@@ -497,9 +497,13 @@ like Rack::Auth, OmniAuth, etc.
Precious::App.set(:wiki_options, {:universal_toc => false})
run Precious::App
## Windows Filename Validation
## WINDOWS FILENAME VALIDATION
Note that filenames on windows must not contain any of the following characters `\ / : * ? " < > |`. See [this support article](http://support.microsoft.com/kb/177506) for details.
## LIB.SO ERROR
`Could not open library 'lib.so'` may be solved by installing `python-devel` on Fedora or `python-dev` on Ubuntu.
## CONTRIBUTE
If you'd like to hack on Gollum, start by forking my repo on GitHub:
@@ -520,14 +524,16 @@ your changes merged back into core is as follows:
1. Send a pull request to the github/gollum project.
## RELEASING
Update VERSION in lib/gollum.rb
$ rake gemspec
$ git tag vX.Y.Z
$ git push origin vX.Y.Z
$ gem build gollum.gemspec
$ gem push gollum-X.Y.Z.gem
## BUILDING THE GEM FROM MASTER
$ gem uninstall -aix gollum
$ gem uninstall -aIx gollum
$ git clone https://github.com/github/gollum.git
$ cd gollum
gollum$ rake build
gollum$ gem install pkg/gollum*.gem
gollum$ gem install --no-ri --no-rdoc pkg/gollum*.gem
+2 -2
View File
@@ -65,8 +65,8 @@ opts = OptionParser.new do |opts|
wiki_options[:live_preview] = false
end
opts.on("--no-mathjax", "Disables mathjax.") do
options['mathjax'] = false
opts.on("--mathjax", "Enables mathjax.") do
options['mathjax'] = true
end
end
+4 -4
View File
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 1.8.7"
s.name = 'gollum'
s.version = '2.1.0'
s.date = '2012-08-01'
s.version = '2.2.1'
s.date = '2012-09-02'
s.rubyforge_project = 'gollum'
s.summary = "A simple, Git-powered wiki."
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
s.add_dependency('github-markup', [">= 0.7.0", "< 1.0.0"])
s.add_dependency('github-markdown')
s.add_dependency('pygments.rb', "~> 0.2.0")
s.add_dependency('posix-spawn', "~> 0.3.0")
s.add_dependency('escape_utils', "0.2.4")
s.add_dependency('sinatra', "~> 1.0")
s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"])
s.add_dependency('sanitize', "~> 2.0.0")
@@ -37,7 +37,7 @@ Gem::Specification.new do |s|
s.add_development_dependency('RedCloth')
s.add_development_dependency('mocha')
s.add_development_dependency('org-ruby', '~>0.6.3')
s.add_development_dependency('org-ruby', '~> 0.7.0')
s.add_development_dependency('shoulda')
s.add_development_dependency('rack-test')
s.add_development_dependency('wikicloth', '~>0.8.0')
+2 -1
View File
@@ -20,9 +20,10 @@ require File.expand_path('../gollum/markup', __FILE__)
require File.expand_path('../gollum/sanitization', __FILE__)
require File.expand_path('../gollum/tex', __FILE__)
require File.expand_path('../gollum/web_sequence_diagram', __FILE__)
require File.expand_path('../gollum/frontend/uri_encode_component', __FILE__)
module Gollum
VERSION = '2.1.0'
VERSION = '2.2.1'
def self.assets_path
::File.expand_path('gollum/frontend/public', ::File.dirname(__FILE__))
+7 -3
View File
@@ -100,9 +100,13 @@ module Gollum
tree.blobs.each do |blob|
next if page_path_scheduled_for_deletion?(index.tree, fullpath)
file = blob.name.downcase.sub(/\.\w+$/, '')
file_ext = ::File.extname(blob.name).sub(/^\./, '')
if downpath == file && !(allow_same_ext && file_ext == ext)
existing_file = blob.name.downcase.sub(/\.\w+$/, '')
existing_file_ext = ::File.extname(blob.name).sub(/^\./, '')
new_file_ext = ::File.extname(path).sub(/^\./, '')
if downpath == existing_file && !(allow_same_ext && new_file_ext == existing_file_ext)
raise DuplicatePageError.new(dir, blob.name, path)
end
end
+38 -32
View File
@@ -9,7 +9,6 @@ require 'gollum/frontend/views/layout'
require 'gollum/frontend/views/editable'
require 'gollum/frontend/views/has_page'
require File.expand_path '../uri_encode_component', __FILE__
require File.expand_path '../helpers', __FILE__
# Fix to_url
@@ -82,23 +81,34 @@ module Precious
end
before do
@base_url = settings.wiki_options.has_key?(:base_url) ? settings.wiki_options[:base_url] : url('/')
@base_url = url('/', false).chomp('/')
settings.wiki_options.merge!({ :base_path => @base_url }) unless settings.wiki_options.has_key? :base_path
end
get '/' do
show_page_or_file('Home')
redirect File.join(settings.wiki_options[:base_path].to_s, 'Home')
end
# name, path, version
def wiki_page( name, path = nil, version = nil)
path = name if path.nil?
# Removes all slashes from the start of string.
def clean_url url
return url if url.nil?
url.gsub('%2F','/').gsub(/^\/+/,'')
end
# path is set to name if path is nil.
# if path is 'a/b' and a and b are dirs, then
# path must have a trailing slash 'a/b/' or
# extract_path will trim path to 'a'
# name, path, version
def wiki_page(name, path = nil, version = nil, exact = true)
path = name if path.nil?
name = extract_name(name)
path = extract_path(path)
path = '/' if exact && path.nil?
wiki = wiki_new
OpenStruct.new(:wiki => wiki, :page => wiki.paged(name, path, version),
OpenStruct.new(:wiki => wiki, :page => wiki.paged(name, path, exact, version),
:name => name, :path => path)
end
@@ -132,19 +142,19 @@ module Precious
mustache :edit
end
else
redirect to("/create/#{CGI.escape(@name)}")
redirect to("/create/#{encodeURIComponent(@name)}")
end
end
post '/edit/*' do
wikip = wiki_page(CGI.unescape(params[:page]), sanitize_empty_params(params[:path]))
path = wikip.path
wiki = wikip.wiki
page = wikip.page
rename = params[:rename].to_url if params[:rename]
name = rename || page.name
committer = Gollum::Committer.new(wiki, commit_message)
commit = {:committer => committer}
path = '/' + clean_url(sanitize_empty_params(params[:path])).to_s
page_name = CGI.unescape(params[:page])
wiki = wiki_new
page = wiki.paged(page_name, path, exact = true)
rename = params[:rename].to_url if params[:rename]
name = rename || page.name
committer = Gollum::Committer.new(wiki, commit_message)
commit = {:committer => committer}
update_wiki_page(wiki, page, params[:content], commit, name, params[:format])
update_wiki_page(wiki, page.header, params[:header], commit) if params[:header]
@@ -168,9 +178,8 @@ module Precious
end
get '/create/*' do
splat = params[:splat].first
wikip = wiki_page(extract_name(splat).to_url, splat)
@name = wikip.name
wikip = wiki_page(params[:splat].first.gsub('+', '-'))
@name = wikip.name.to_url
@path = wikip.path
page = wikip.page
@@ -184,6 +193,7 @@ module Precious
post '/create' do
name = params[:page].to_url
path = sanitize_empty_params(params[:path])
path = '' if path.nil?
format = params[:format].intern
# write_page is not directory aware so use wiki_options to emulate dir support.
@@ -192,8 +202,7 @@ module Precious
begin
wiki.write_page(name, format, params[:content], commit_message)
page = wiki.page(name)
redirect to("/#{page.escaped_url_path}")
redirect to("/#{clean_url(CGI.escape(::File.join(path,name)))}")
rescue Gollum::DuplicatePageError => e
@message = "Duplicate page: #{e.message}"
mustache :error
@@ -273,22 +282,17 @@ module Precious
mustache :compare
end
get '/_tex.png' do
content_type 'image/png'
formula = Base64.decode64(params[:data])
Gollum::Tex.render_formula(formula)
end
get %r{^/(javascript|css|images)} do
halt 404
end
get %r{/(.+?)/([0-9a-f]{40})} do
file_path = params[:captures][0]
name = extract_name(file_path)
path = extract_path(file_path)
version = params[:captures][1]
if page = wiki_page(name, path, version).page
wikip = wiki_page(file_path, file_path, version)
name = wikip.name
path = wikip.path
if page = wikip.page
@page = page
@name = name
@content = page.formatted_data
@@ -337,7 +341,9 @@ module Precious
path = extract_path(fullpath)
wiki = wiki_new
if page = wiki.paged(name, path)
path = '/' if path.nil?
if page = wiki.paged(name, path, exact = true)
@page = page
@name = name
@editable = true
@@ -350,7 +356,7 @@ module Precious
file.raw_data
else
page_path = [path, name].compact.join('/')
redirect to("/create/#{CGI.escape(page_path).gsub('%2F','/')}")
redirect to("/create/#{clean_url(encodeURIComponent(page_path))}")
end
end
@@ -167,7 +167,7 @@
setActiveLanguage: function( name ) {
// On first load _ACTIVE_LANG.length is 0 and evtChangeFormat isn't called.
if ( LanguageDefinition._ACTIVE_LANG.length <= 0 ) {
if ( LanguageDefinition._ACTIVE_LANG != null && LanguageDefinition._ACTIVE_LANG.length <= 0 ) {
FormatSelector.updateCommitMessage( name );
}
@@ -196,7 +196,9 @@
LanguageDefinition.getHookFunctionFor("activate")();
}
function hotkey( cmd ) {
function hotkey( e, cmd ) {
e.preventDefault();
var def = LanguageDefinition.getDefinitionFor( cmd );
if ( typeof def == 'object' ) {
FunctionBar.executeAction( def );
@@ -205,8 +207,17 @@
return false;
}
Mousetrap.bind(['command+b', 'ctrl+b'], function(){ hotkey('function-bold'); });
Mousetrap.bind(['command+i', 'ctrl+i'], function(){ hotkey('function-italic'); });
Mousetrap.bind(['command+1', 'ctrl+1'], function( e ){ hotkey( e, 'function-h1' ); });
Mousetrap.bind(['command+2', 'ctrl+2'], function( e ){ hotkey( e, 'function-h2' ); });
Mousetrap.bind(['command+3', 'ctrl+3'], function( e ){ hotkey( e, 'function-h3' ); });
Mousetrap.bind(['command+b', 'ctrl+b'], function( e ){ hotkey( e, 'function-bold' ); });
Mousetrap.bind(['command+i', 'ctrl+i'], function( e ){ hotkey( e, 'function-italic' ); });
Mousetrap.bind(['command+s', 'ctrl+s'], function( e ){
e.preventDefault();
$("#gollum-editor-submit").trigger("click");
return false;
});
} );
} else {
LanguageDefinition._ACTIVE_LANG = name;
@@ -4,7 +4,7 @@ $(document).ready(function() {
var ok = confirm($(this).data('confirm'));
if ( ok ) {
var loc = window.location;
loc = baseUrl + 'delete' + loc.pathname
loc = baseUrl + '/delete' + loc.pathname.replace(baseUrl,'');
window.location = loc;
}
// Don't navigate on cancel.
@@ -138,10 +138,10 @@ $(document).ready(function() {
var msg = 'Renamed ' + oldName + ' to ' + newName;
jQuery.ajax( {
type: 'POST',
url: baseUrl + 'edit/' + oldName,
url: baseUrl + '/edit/' + oldName,
data: { path: path, rename: newName, page: oldName, message: msg },
success: function() {
window.location = baseUrl + encodeURIComponent(newName);
window.location = baseUrl + '/' + encodeURIComponent(newName);
}
});
}
@@ -154,14 +154,6 @@ $(document).ready(function() {
$('#minibutton-new-page').click(function(e) {
e.preventDefault();
var path = location.pathname;
// ensure there's more than one slash in pathname.
if (path.split('/').length > 2) {
path = path.substr(path.lastIndexOf('/')+1) + '/';
} else {
path = '';
}
$.GollumDialog.init({
title: 'Create New Page',
fields: [
@@ -169,7 +161,7 @@ $(document).ready(function() {
id: 'name',
name: 'Page Name',
type: 'text',
defaultValue: path || ''
defaultValue: ''
}
],
OK: function( res ) {
@@ -177,7 +169,7 @@ $(document).ready(function() {
if ( res['name'] ) {
name = res['name'];
}
window.location = baseUrl + encodeURIComponent(name);
window.location = baseUrl + '/' + encodeURIComponent(name);
}
});
});
@@ -2,6 +2,10 @@ body {
overflow: hidden;
}
#editor .ace_sb {
overflow-y: auto !important;
}
#darkness {
visibility: hidden;
position: absolute;
@@ -49,7 +53,7 @@ so editor doesn't display in the background. */
#contentframe {
margin: 0 auto;
overflow: visible;
width: 80%;
width: 90%;
}
#previewframe {
@@ -63,14 +67,35 @@ so editor doesn't display in the background. */
right: 0;
}
.editor_bg {
position: fixed;
top: 0;
margin: 0;
padding: 0;
background: black;
width: 50%;
height: 100%;
z-index: -2;
}
.toolpanel_bg {
position: fixed;
background: #666;
top: 0;
height: 30px;
width: 100%;
padding: 5px 0;
margin: 0;
z-index: -1;
}
/* -- Start from notepag.es -- */
.toolpanel {
position: fixed;
background: #666;
top: 0;
height: 30px;
right: 20px;
width: 80px;
width: 50%;
vertical-align: middle;
padding: 5px 0;
margin: 0;
@@ -16,6 +16,9 @@
<a id='toggle' class='edit' href='javascript:void(0)' onclick='jsm.toggleLeftRight();'><img src='images/lr_24.png' alt='Toggle left to right' title='Toggle left to right'></a>
</div>
<div id='editor_bg' class='editor_bg'></div>
<div class='toolpanel_bg'></div>
<div id='commenttoolpanel' class='toolpanel edit' style='width: 500px; right: 0px; '>
<a id='savecommentconfirm' class='edit'><img src='images/savecomment_24.png' alt='Confirm save with comment' title='Confirm save with comment'></a>
<a id='commentcancel' class='edit'><img src='images/cancel_24.png' alt='Cancel save with comment' title='Cancel save with comment'></a>
@@ -36,11 +39,11 @@ var require = {
<script src='js/sundown.js'></script>
<script src='js/md_sundown.js'></script>
<script src='js/livepreview.js'></script>
<script>(function(d,j){
<!--<script>(function(d,j){
j = d.createElement('script');
j.src = 'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
(d.head || d.getElementsByTagName('head')[0]).appendChild(j);
}(document));
</script>
</script>-->
</body>
</html>
@@ -62,9 +62,10 @@ initAce( commentEditor, commentEditorSession );
var baseUrl = location.pathname.split('/').slice(0,-2).join('/');
// RegExp from http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript
// Returns value on success and undefined on failure.
$.key = function( key ) {
var value = new RegExp( '[\\?&]' + key + '=([^&#]*)' ).exec( location.href );
return ( !value ) ? 0 : value[ 1 ] || 0;
return ( !value ) ? undefined : value[ 1 ] || undefined;
}
// True if &create=true
@@ -73,10 +74,6 @@ var create = $.key( 'create' );
var pageName = $.key( 'page' );
var pathName = $.key( 'path' );
if ( pathName === 0 ) {
pathName = undefined;
}
defaultCommitMessage = function() {
var msg = pageName + ' (markdown)';
@@ -97,13 +94,23 @@ $.save = function( commitMessage ) {
var markdown = 'markdown';
var txt = editorSession.getValue();
var msg = defaultCommitMessage();
var newLocation = location.protocol + '//' + location.host + baseUrl;
var newLocation = baseUrl;
if (pathName) {
function clean( str ) {
return str.replace(/^\/+/, '/');
}
// 'a%2Fb' => a/b
if ( pathName ) {
pathName = unescape( pathName );
newLocation += '/' + pathName;
pathName = pathName + '/'; // pathName must end with /
pathName = clean( pathName );
}
newLocation += '/' + pageName;
newLocation = clean( newLocation );
// if &create=true then handle create instead of edit.
if ( create ) {
@@ -331,10 +338,23 @@ var applyTimeout = function () {
/* Load markdown from /data/page into the ace editor.
~-1 == false; !~-1 == true;
*/
if ( !~location.host.indexOf('github.com') ) {
if ( !~ location.host.indexOf( 'github.com' ) ) {
// returns unescaped key with leading slashes removed
function key_no_leading_slash( key ) {
return unescape( $.key( key ) || '' ).replace( /^\/+/, '' );
}
// ensure leading / is removed from path and that it ends with /
var path = key_no_leading_slash( 'path' );
// don't append '/' if path is empty from removing leading slash
if ( path !== '' && path.charAt( path.length - 1 ) !== '/' ) {
path += '/';
}
jQuery.ajax( {
type: 'GET',
url: baseUrl + '/data/' + $.key( 'page' ),
url: baseUrl + '/data/' + path + key_no_leading_slash( 'page' ),
success: function( data ) {
editorSession.setValue( data );
}
@@ -395,7 +415,8 @@ var applyTimeout = function () {
var heightHalf = height / 2;
// height minus 50 so the end of document text doesn't flow off the page.
var editorContainerStyle = 'width:' + widthHalf + 'px;' +
// + 15 for scroll bar
var editorContainerStyle = 'width:' + (widthHalf + 15) + 'px;' +
'height:' + (height - 50) + 'px;' +
'left:' + (leftRight === false ? widthHalf + 'px;' : '0px;') +
'top:' + '40px;'; // use 40px for tool menu
@@ -406,12 +427,12 @@ var applyTimeout = function () {
var previewStyle = 'width:' + (widthHalf - 2 - 10) + 'px;' +
'height:' + height + 'px;' +
'left:' + (leftRight === false ? '10px;' : widthHalf + 'px;') +
'top:' + '0px;';
// preview panel top is equal to height of comment tool panel (40px) + 1
'top:41px;';
cssSet( preview, previewStyle );
// Resize tool panel
var toolPanelStyle = 'width:' + widthHalf + 'px;' +
'left:' + (leftRight === false ? widthHalf + 'px;' : '0px;');
var toolPanelStyle = 'width:50%;';
cssSet( toolPanel, toolPanelStyle );
// Resize comment related elements.
@@ -426,10 +447,12 @@ var applyTimeout = function () {
cssSet( commentEditorContainer, commentEditorContainerStyle );
commentEditor.resize();
var commentToolPanelHeight = height / 4 - 40;
// In top subtract height (40px) of comment tool panel.
var commentToolPanelStyle = 'width:' + widthHalf + 'px;' +
'left:' + widthFourth + 'px;' +
'top:' + (height / 4 - 40) + 'px;' +
'top:' + commentToolPanelHeight + 'px;' +
commentHidden;
cssSet( commentToolPanel, commentToolPanelStyle );
@@ -442,6 +465,11 @@ var applyTimeout = function () {
win.jsm.resize = resize;
// remove editor_bg after loading because
// it'll cause problems if toggle left right is used
var ebg = doc.getElementById('editor_bg');
ebg.parentNode.removeChild(ebg);
/*
Resize can be called an absurd amount of times
and will crash the page without debouncing.
@@ -6,11 +6,11 @@
<li class="minibutton">
{{>searchbar}}
</li>
<li class="minibutton"><a href="{{base_url}}{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/{{escaped_url_path}}"
class="action-view-page">View Page</a></li>
<li class="minibutton"><a href="{{base_url}}edit/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/edit/{{escaped_url_path}}"
class="action-edit-page">Edit Page</a></li>
<li class="minibutton"><a href="{{base_url}}history/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/history/{{escaped_url_path}}"
class="action-page-history">Page History</a></li>
</ul>
</div>
@@ -23,10 +23,10 @@
{{#show_revert}}
<ul class="actions">
<li class="minibutton"><a href="{{base_url}}history/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/history/{{escaped_url_path}}"
class="action-page-history">Back to Page History</a></li>
<li class="minibutton">
<form name="gollum-revert" action="{{base_url}}revert/{{escaped_url_path}}/{{before}}/{{after}}" method="post" id="gollum-revert-form">
<form name="gollum-revert" action="{{base_url}}/revert/{{escaped_url_path}}/{{before}}/{{after}}" method="post" id="gollum-revert-form">
<a href="#" class="gollum-revert-button">Revert Changes</a>
</form>
</li>
@@ -49,7 +49,7 @@
</div>
<div id="footer">
<ul class="actions">
<li class="minibutton"><a href="{{base_url}}history/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/history/{{escaped_url_path}}"
class="action-page-history">Back to Page History</a></li>
{{#show_revert}}
<li class="minibutton">
+2 -2
View File
@@ -2,9 +2,9 @@
<div id="head">
<h1>Editing <strong>{{title}}</strong></h1>
<ul class="actions">
<li class="minibutton"><a href="{{base_url}}{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/{{escaped_url_path}}"
class="action-view-page">View Page</a></li>
<li class="minibutton"><a href="{{base_url}}history/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/history/{{escaped_url_path}}"
class="action-page-history">Page History</a></li>
</ul>
</div>
@@ -1,9 +1,9 @@
<div id="gollum-editor" data-escaped-name="{{escaped_name}}" class="{{#is_create_page}}create{{/is_create_page}}{{#is_edit_page}}edit{{/is_edit_page}}">
{{#is_create_page}}
<form name="gollum-editor" action="{{base_url}}create" method="post">
<form name="gollum-editor" action="{{base_url}}/create" method="post">
{{/is_create_page}}
{{#is_edit_page}}
<form name="gollum-editor" action="{{base_url}}edit/{{escaped_name}}" method="post">
<form name="gollum-editor" action="{{base_url}}/edit/{{escaped_name}}" method="post">
{{/is_edit_page}}
<fieldset id="gollum-editor-fields">
{{#is_create_page}}
@@ -126,7 +126,7 @@
<span class="jaws"><br></span>
<input type="submit" id="gollum-editor-submit" value="Save" title="Save current changes">
<a href="{{base_url}}preview" id="gollum-editor-preview" class="minibutton" title="Preview this Page">Preview</a>
<a href="{{base_url}}/preview" id="gollum-editor-preview" class="minibutton" title="Preview this Page">Preview</a>
</fieldset>
</form>
</div>
@@ -2,9 +2,9 @@
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=utf-8">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/gollum.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/template.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/_styles.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/gollum.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/template.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/_styles.css" media="all">
<title>{{title}}</title>
</head>
<body>
@@ -12,7 +12,7 @@
<div id="home_button">
<ul class="actions">
<li class="minibutton">
<a href="{{base_url}}" class="action-edit-page">Home</a>
<a href="{{base_url}}/" class="action-edit-page">Home</a>
</li>
</ul>
</div>
@@ -5,9 +5,9 @@
<li class="minibutton">
{{>searchbar}}
</li>
<li class="minibutton"><a href="{{base_url}}{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/{{escaped_url_path}}"
class="action-view-page">View Page</a></li>
<li class="minibutton"><a href="{{base_url}}edit/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/edit/{{escaped_url_path}}"
class="action-edit-page">Edit Page</a></li>
</ul>
</div>
@@ -19,7 +19,7 @@
</ul>
<form name="compare-versions" id="version-form" method="post"
action="{{base_url}}compare/{{escaped_url_path}}">
action="{{base_url}}/compare/{{escaped_url_path}}">
<fieldset>
<table>
<tbody>
@@ -39,7 +39,7 @@
<td class="commit-name">
<span class="time-elapsed">{{date}}:</span>&nbsp;
{{message}}
[<a href="{{base_url}}{{escaped_url_path}}/{{id}}" title="View commit">{{id7}}</a>]
[<a href="{{base_url}}/{{escaped_url_path}}/{{id}}" title="View commit">{{id7}}</a>]
</td>
</tr>
{{/versions}}
+11 -11
View File
@@ -2,22 +2,22 @@
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=utf-8">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/gollum.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/editor.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/dialog.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}css/template.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/gollum.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/editor.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/dialog.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/template.css" media="all">
<!--[if IE 7]>
<link rel="stylesheet" type="text/css" href="{{base_url}}css/ie7.css" media="all">
<link rel="stylesheet" type="text/css" href="{{base_url}}/css/ie7.css" media="all">
<![endif]-->
<script>var baseUrl = '{{base_url}}'</script>
<script type="text/javascript" src="{{base_url}}javascript/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="{{base_url}}javascript/mousetrap.min.js"></script>
<script type="text/javascript" src="{{base_url}}javascript/gollum.js"></script>
<script type="text/javascript" src="{{base_url}}javascript/gollum.dialog.js"></script>
<script type="text/javascript" src="{{base_url}}javascript/gollum.placeholder.js"></script>
<script type="text/javascript" src="{{base_url}}javascript/editor/gollum.editor.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/mousetrap.min.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/gollum.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/gollum.dialog.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/gollum.placeholder.js"></script>
<script type="text/javascript" src="{{base_url}}/javascript/editor/gollum.editor.js"></script>
{{#mathjax}}<script>(function(d,j){
j = d.createElement('script');
j.src = 'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
+13 -6
View File
@@ -1,3 +1,10 @@
<script>
Mousetrap.bind(['e'], function( e ) {
e.preventDefault();
window.location = "/edit" + window.location.pathname;
return false;
});
</script>
<div id="wiki-wrapper" class="page">
<div id="head">
<h1>{{title}}</h1>
@@ -5,21 +12,21 @@
<li class="minibutton">
{{>searchbar}}
</li>
<li class="minibutton"><a href="{{base_url}}"
<li class="minibutton"><a href="{{base_url}}/"
class="action-edit-page">Home</a></li>
<li class="minibutton"><a href="{{base_url}}pages"
<li class="minibutton"><a href="{{base_url}}/pages"
class="action-all-pages">All</a></li>
<li class="minibutton"><a href="{{base_url}}fileview"
<li class="minibutton"><a href="{{base_url}}/fileview"
class="action-all-pages">Files</a></li>
<li class="minibutton" class="jaws">
<a href="#" id="minibutton-new-page">New</a></li>
<li class="minibutton" class="jaws">
<a href="#" id="minibutton-rename-page">Rename</a></li>
{{#editable}}
<li class="minibutton"><a href="{{base_url}}edit/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/edit/{{escaped_url_path}}"
class="action-edit-page">Edit</a></li>
{{/editable}}
<li class="minibutton"><a href="{{base_url}}history/{{escaped_url_path}}"
<li class="minibutton"><a href="{{base_url}}/history/{{escaped_url_path}}"
class="action-page-history">History</a></li>
</ul>
</div>
@@ -62,7 +69,7 @@
<div id="footer">
<p id="last-edit">Last edited by <b>{{author}}</b>, {{date}}</p>
<p>
<a id="delete-link" href="{{base_url}}{{escaped_url_path}}" data-confirm="Are you sure you want to delete this page?"><span>Delete this Page</span></a>
<a id="delete-link" href="{{base_url}}/{{escaped_url_path}}" data-confirm="Are you sure you want to delete this page?"><span>Delete this Page</span></a>
</p>
</div>
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<li class="minibutton">
{{>searchbar}}
</li>
<li class="minibutton"><a href="{{base_url}}"
<li class="minibutton"><a href="{{base_url}}/"
class="action-edit-page">Home</a></li>
<li class="minibutton" class="jaws">
<a href="#" id="minibutton-new-page">New</a>
@@ -5,7 +5,7 @@
<li class="minibutton">
{{>searchbar}}
</li>
<li class="minibutton"><a href="{{base_url}}"
<li class="minibutton"><a href="{{base_url}}/"
class="action-edit-page">Home</a></li>
</ul>
</div>
@@ -15,7 +15,7 @@
<ul>
{{#results}}
<li>
<a href="{{base_url}}{{name}}">{{name}}</a>
<a href="{{base_url}}/{{name}}">{{name}}</a>
<span class="count">({{count}} matches)</span>
</li>
{{/results}}
@@ -1,5 +1,5 @@
<div id="searchbar">
<form action="{{base_url}}search" method="get" id="search-form">
<form action="{{base_url}}/search" method="get" id="search-form">
<div id="searchbar-fauxtext">
<input type="text" name="q" id="search-query" value="Search&hellip;" autocomplete="off">
<a href="#" id="search-submit" title="Search this wiki">
+10 -2
View File
@@ -1,3 +1,4 @@
# ~*~ encoding: utf-8 ~*~
=begin
Copyright 2006-2008 the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -39,7 +40,12 @@ end
# define charCodeAt on String
class String
def charCodeAt(k)
return self[k].ord
# use scan, nil check, and unpack instead of ord for 1.8
# 1.9 can simply use self[k].ord
# http://stackoverflow.com/questions/7793177/split-utf8-string-regardless-of-ruby-version
c = self.scan(/./mu)[k]
return nil if c.nil?
c.unpack('U')[0]
end
end
@@ -147,6 +153,7 @@ def Encode(uri, unescape)
k = -1;
while ((k+=1) < uriLength) do
cc1 = uri.charCodeAt(k);
next if cc1.nil?
if (self.send(unescape, cc1))
result[index] = cc1;
index += 1
@@ -163,10 +170,11 @@ def Encode(uri, unescape)
end
end
end
# use .compact to get rid of nils from charCodeAt
# return %StringFromCharCodeArray(result);
# 'c' = 8 bit signed char
# http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-pack
return result.pack 'c*'
return result.compact.pack 'c*'
end
end # class << self
end # module
+5
View File
@@ -6,6 +6,11 @@ module Precious
attr_reader :page, :content
# return path set in app.rb not @page.path
def path
@path
end
def title
"#{@page.title}"
end
+11
View File
@@ -79,6 +79,17 @@ module Precious
def mathjax
@mathjax
end
# Access to embedded metadata.
#
# Examples
#
# {{#metadata}}{{name}}{{/metadata}}
#
# Returns Hash.
def metadata
@page.metadata
end
end
end
end
+4 -4
View File
@@ -12,14 +12,14 @@ module Precious
def breadcrumb
if @path
path = Pathname.new(@path)
breadcrumb = [%{<a href="#{@base_url}pages/">Home</a>}]
breadcrumb = [%{<a href="#{@base_url}/pages/">Home</a>}]
path.descend do |crumb|
title = crumb.basename
if title == path.basename
breadcrumb << title
else
breadcrumb << %{<a href="#{@base_url}pages/#{crumb}/">#{title}</a>}
breadcrumb << %{<a href="#{@base_url}/pages/#{crumb}/">#{title}</a>}
end
end
@@ -39,7 +39,7 @@ module Precious
if page_path.include?('/')
folder = page_path.split('/').first
folder_path = @path ? "#{@path}/#{folder}" : folder
folder_link = %{<li><a href="#{@base_url}pages/#{folder_path}/" class="folder">#{folder}</a></li>}
folder_link = %{<li><a href="#{@base_url}/pages/#{folder_path}/" class="folder">#{folder}</a></li>}
unless folder_links.include?(folder_link)
folder_links << folder_link
@@ -47,7 +47,7 @@ module Precious
folder_link
end
elsif page_path != ".gitkeep"
%{<li><a href="#{@base_url}#{page.escaped_url_path}" class="file">#{page.name}</a></li>}
%{<li><a href="#{@base_url}/#{page.escaped_url_path}" class="file">#{page.name}</a></li>}
end
}.compact.join("\n")
else
+56 -24
View File
@@ -3,10 +3,15 @@ require 'cgi'
require 'pygments'
require 'base64'
# initialize Pygments
Pygments.start
module Gollum
class Markup
attr_accessor :toc
attr_reader :metadata
# Initialize a new Markup object.
#
# page - The Gollum::Page.
@@ -27,6 +32,7 @@ module Gollum
@wsdmap = {}
@premap = {}
@toc = nil
@metadata = nil
end
# Render the content with Gollum wiki syntax on top of the file's own
@@ -43,6 +49,7 @@ module Gollum
@wiki.sanitizer
data = @data.dup
data = extract_metadata(data)
data = extract_code(data)
data = extract_tex(data)
data = extract_wsd(data)
@@ -80,14 +87,15 @@ module Gollum
def process_headers(doc)
toc = nil
doc.css('h1,h2,h3,h4,h5,h6').each do |h|
id = CGI::escape(h.content.gsub(' ','-'))
id = encodeURIComponent(h.content.gsub(' ','-'))
level = h.name.gsub(/[hH]/,'').to_i
# Add anchors
anchor = Nokogiri::XML::Node.new('a', doc)
anchor['class'] = 'anchor'
anchor['id'] = id
anchor['href'] = '#' + id
# % -> %25 so anchors work on Firefox. See issue #475
anchor['href'] = '#' + id.gsub('%', '%25')
h.add_child(anchor)
# Build TOC
@@ -105,7 +113,8 @@ module Gollum
tail_level -= 1
end
node = Nokogiri::XML::Node.new('li', doc)
node.add_child("<a href='##{id}'>#{h.content}</a>")
# % -> %25 so anchors work on Firefox. See issue #475
node.add_child("<a href='##{id.gsub('%', '%25')}'>#{h.content}</a>")
tail.add_child(node)
end
toc = toc.to_xhtml if toc != nil
@@ -146,23 +155,7 @@ module Gollum
def process_tex(data)
@texmap.each do |id, spec|
type, tex = *spec
# Obtain the formula with parameters
out = nil
begin
width, height, align, base64 = Gollum::Tex.render_formula(tex, true)
# TODO: Should we load the binary inside the html?
#out = %{<img width="#{width}" height="#{height}" style="vertical-align: #{align}px;" src="data:image/png;base64,\n#{base64}" alt="#{CGI.escapeHTML(tex)}" />}
# Use the alignment values from the formula rendering but still use the call to '_tex.png'. Although it will call render_formula()
# again, it will use the already cached formula and it might have some advantages from the point of view of browser caching (really not sure here).
out = %{<img width="#{width}" height="#{height}" style="vertical-align: #{align}px;" src="#{::File.join(@wiki.base_path, '_tex.png')}?type=#{type}&data=#{Base64.encode64(tex).chomp}" alt="#{CGI.escapeHTML(tex)}" />}
rescue # In case of error
out = CGI.escapeHTML(tex)
end
data.gsub!(id, out)
data.gsub!(id, Gollum::Tex.to_html(tex, type))
end
data
end
@@ -219,7 +212,7 @@ module Gollum
if is_preformatted?(data, id)
data.gsub!(id, "[[#{tag}]]")
else
data.gsub!(id, process_tag(tag))
data.gsub!(id, process_tag(tag).gsub('%2F', '/'))
end
end
data
@@ -399,7 +392,7 @@ module Gollum
link_name = @wiki.page_class.cname(page.name)
presence = "present"
end
link = ::File.join(@wiki.base_path, CGI.escape(link_name))
link = ::File.join(@wiki.base_path, page ? page.escaped_url_path : CGI.escape(link_name))
%{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
end
end
@@ -432,13 +425,23 @@ module Gollum
# Find a page from a given cname. If the page has an anchor (#) and has
# no match, strip the anchor and try again.
#
# cname - The String canonical page name.
# cname - The String canonical page name including path.
#
# Returns a Gollum::Page instance if a page is found, or an Array of
# [Gollum::Page, String extra] if a page without the extra anchor data
# is found.
def find_page_from_name(cname)
if page = @wiki.page(cname)
slash = cname.rindex('/')
unless slash.nil?
name = cname[slash+1..-1]
path = cname[0..slash]
page = @wiki.paged(name, path)
else
page = @wiki.paged(cname, '/')
end
if page
return page
end
if pos = cname.index('#')
@@ -565,6 +568,35 @@ module Gollum
data
end
#########################################################################
#
# Metadata
#
#########################################################################
# Extract metadata for data and build metadata table. Metadata
# is content found between markers, and must
# be a valid YAML mapping.
#
# Because ri and ruby 1.8.7 are awesome, the markers can't
# be included in this documentation without triggering
# `Unhandled special: Special: type=17`
# Please read the source code for the exact markers
#
# Returns the String of formatted data with metadata removed.
def extract_metadata(data)
@metadata ||= {}
# The markers are `<!-- ---` and `-->`
data.gsub(/\<\!--+\s+---(.*?)--+\>/m) do
yaml = @wiki.sanitizer.clean($1)
hash = YAML.load(yaml)
if Hash === hash
@metadata.update(hash)
end
''
end
end
# Hook for getting the formatted value of extracted tag data.
#
# type - Symbol value identifying what type of data is being extracted.
+14 -4
View File
@@ -217,6 +217,14 @@ module Gollum
markup_class.toc
end
# Public: Embedded metadata.
#
# Returns Hash of metadata.
def meta_data()
formatted_data if markup_class.metadata == nil
markup_class.metadata
end
# Public: The format of the page.
#
# Returns the Symbol format of the page. One of:
@@ -319,7 +327,7 @@ module Gollum
# Returns the String canonical name.
def self.cname(name, char_white_sub = '-', char_other_sub = '-')
name.respond_to?(:gsub) ?
name.gsub(%r{\s},char_white_sub).gsub(%r{[/<>+]}, char_other_sub) :
name.gsub(%r{\s},char_white_sub).gsub(%r{[<>+]}, char_other_sub) :
''
end
@@ -364,9 +372,9 @@ module Gollum
# version - The String version ID to find.
#
# Returns a Gollum::Page or nil if the page could not be found.
def find(name, version, dir = nil)
def find(name, version, dir = nil, exact = false)
map = @wiki.tree_map_for(version.to_s)
if page = find_page_in_tree(map, name, dir)
if page = find_page_in_tree(map, name, dir, exact)
page.version = version.is_a?(Grit::Commit) ?
version : @wiki.commit_for(version)
page.historical = page.version.to_s == version.to_s
@@ -383,12 +391,14 @@ module Gollum
# to be in. The string should
#
# Returns a Gollum::Page or nil if the page could not be found.
def find_page_in_tree(map, name, checked_dir = nil)
def find_page_in_tree(map, name, checked_dir = nil, exact = false)
return nil if !map || name.to_s.empty?
if checked_dir = BlobEntry.normalize_dir(checked_dir)
checked_dir.downcase!
end
checked_dir = '' if exact && checked_dir.nil?
map.each do |entry|
next if entry.name.to_s.empty?
next unless checked_dir.nil? || entry.dir.downcase == checked_dir
+7 -359
View File
@@ -1,366 +1,14 @@
require 'fileutils'
require 'shellwords'
require 'tmpdir'
require 'posix/spawn'
require 'base64'
require 'escape_utils'
module Gollum
module Tex
class Error < StandardError; end
TEX_URL = "http://www.mathtran.org/cgi-bin/toy/"
TEX_SIZES = { :inline => 2, :block => 4 }
extend POSIX::Spawn
Template = <<-EOS
\\documentclass[11pt]{article}
\\pagestyle{empty}
\\setlength{\\topskip}{0pt}
\\setlength{\\parindent}{0pt}
\\setlength{\\abovedisplayskip}{0pt}
\\setlength{\\belowdisplayskip}{0pt}
\\usepackage{geometry}
\\usepackage{amsfonts}
\\usepackage{amsmath}
\\newsavebox{\\snippetbox}
\\newlength{\\snippetwidth}
\\newlength{\\snippetheight}
\\newlength{\\snippetdepth}
\\newlength{\\pagewidth}
\\newlength{\\pageheight}
\\newlength{\\pagemargin}
\\begin{lrbox}{\\snippetbox}%
\$%s\$
\\end{lrbox}
\\settowidth{\\snippetwidth}{\\usebox{\\snippetbox}}
\\settoheight{\\snippetheight}{\\usebox{\\snippetbox}}
\\settodepth{\\snippetdepth}{\\usebox{\\snippetbox}}
\\setlength\\pagemargin{4pt}
\\setlength\\pagewidth\\snippetwidth
\\addtolength\\pagewidth\\pagemargin
\\addtolength\\pagewidth\\pagemargin
\\setlength\\pageheight\\snippetheight
\\addtolength{\\pageheight}{\\snippetdepth}
\\addtolength\\pageheight\\pagemargin
\\addtolength\\pageheight\\pagemargin
\\newwrite\\foo
\\immediate\\openout\\foo=\\jobname.dimensions
\\immediate\\write\\foo{snippetdepth = \\the\\snippetdepth}
\\immediate\\write\\foo{snippetheight = \\the\\snippetheight}
\\immediate\\write\\foo{snippetwidth = \\the\\snippetwidth}
\\immediate\\write\\foo{pagewidth = \\the\\pagewidth}
\\immediate\\write\\foo{pageheight = \\the\\pageheight}
\\immediate\\write\\foo{pagemargin = \\the\\pagemargin}
\\closeout\\foo
\\geometry{paperwidth=\\pagewidth,paperheight=\\pageheight,margin=\\pagemargin}
\\begin{document}%
\\usebox{\\snippetbox}%
\\end{document}
EOS
class << self
attr_accessor :latex_path
def self.to_html(tex, type = :inline)
tex_uri = EscapeUtils.escape_uri(tex)
tex_alt = EscapeUtils.escape_html(tex)
%{<img src="#{TEX_URL}?D=#{TEX_SIZES[type]};tex=#{tex_uri}" alt="#{tex_alt}">}
end
self.latex_path = 'pdflatex'
def self.check_dependencies!
return if @dependencies_available
if `which pdflatex` == ""
raise Error, "`pdflatex` command not found"
end
if `which gs` == ""
raise Error, "`gs` command not found"
end
if `which pnmcrop` == ""
raise Error, "`pnmcrop` command not found"
end
if `which pnmpad` == ""
raise Error, "`pnmpad` command not found"
end
if `which pnmscale` == ""
raise Error, "`pnmscale` command not found"
end
if `which ppmtopgm` == ""
raise Error, "`ppmtopgm` command not found"
end
if `which pnmgamma` == ""
raise Error, "`pnmgamma` command not found"
end
if `which pnmtopng` == ""
raise Error, "`pnmtopng` command not found"
end
@dependencies_available = true
end
# Render the formula and calculate the correct alignment
# for the image in the html.
#
# This is a ruby implementation of the Perl version described
# at http://tex.stackexchange.com/questions/44486/pixel-perfect-vertical-alignment-of-image-rendered-tex-snippets
#
# The main caveat is that rendering takes quite a bit of processing power,
# which can make the page load slowly if it has to render each time.
# For this reason, the method caches the rendered formula in `/tmp` for reduced
# loading time in subsequent loads.
#
# @param formula the tex formula to render
# @param with_properties, if true it returns an array with a base64
# string with the image, and the alignment values for the image.
# Otherwise it returns the binary image.
def self.render_formula(formula, with_properties=false)
check_dependencies!
render_antialias_bits = 4
render_oversample = 4
display_oversample = 4
gamma = 0.3
if !with_properties
display_oversample = 1
gamma = 0.5
end
oversample = render_oversample * display_oversample
render_dpi = 96*1.2 * 72.27/72 * oversample # This is 1850.112 dpi.
# Cache rendered formula and returned cached version if it exists
# First look for the .cache directory in the home folder
cache_dir = ::File.expand_path("~/.cache")
if not ::File.exists?(cache_dir) or not ::File.directory?(cache_dir)
::Dir.mkdir(cache_dir)
end
# Check that the gollum directory exists inside the cache dir
cache_dir = ::File.join(cache_dir, "gollum")
if not ::File.exists?(cache_dir) or not ::File.directory?(cache_dir)
::Dir.mkdir(cache_dir)
end
# Check for the formula in the cache dir
hash = Digest::SHA1.hexdigest(formula)
cache_file = ::File.join(cache_dir, "tex-#{hash}")
if ::File.exists?(cache_file)
width, height, align, base64 = ::File.open(cache_file, 'rb') { |io| io.read }.split(",")
if with_properties
return width, height, align, base64
else
return Base64.decode64(base64)
end
end
Dir.mktmpdir('tex') do |path|
file = ::File.join(path, "formula")
# --- Write TeX source and compile to PDF.Write snippet into template
::File.open(file + ".tex", 'w') { |f| f.write(Template % formula) }
result = sh_chdir path, "pdflatex",
"-halt-on-error",
"-output-directory=#{path}",
"-output-format=pdf",
"#{file}.tex",
">#{file}.err 2>&1"
# --- Convert PDF to PNM using Ghostscript.
sh "gs",
"-q -dNOPAUSE -dBATCH",
"-dTextAlphaBits=#{render_antialias_bits}",
"-dGraphicsAlphaBits=#{render_antialias_bits}",
"-r#{render_dpi}",
"-sDEVICE=pnmraw",
"-sOutputFile=#{file}.pnm",
"#{file}.pdf"
img_width, img_height = pnm_width_height(file + ".pnm")
# --- Read dimensions file written by TeX during processing.
#
# Example of file contents:
# snippetdepth = 6.50009pt
# snippetheight = 13.53899pt
# snippetwidth = 145.4777pt
# pagewidth = 153.4777pt
# pageheight = 28.03908pt
# pagemargin = 4.0pt
dimensions = {}
::File.open(file + ".dimensions").readlines.each_with_index do |line, i|
if line =~ /^(\S+)\s+=\s+(-?[0-9\.]+)pt$/
dimensions[$1] = Float($2) / 72.27 * render_dpi
else
raise Error, "#{file}.dimensions: invalid line: #{i}"
end
end
# --- Crop bottom, then measure how much was cropped.
sh "pnmcrop -white -bottom #{file}.pnm >#{file}.bottomcrop.pnm"
#raise Error, "`pnmcrop` command failed: #{result}" unless ::File.exist?(file + ".bottomcrop.pnm")
img_width_bottomcrop, img_height_bottomcrop = pnm_width_height("#{file}.bottomcrop.pnm")
bottomcrop = img_height - img_height_bottomcrop
# --- Crop top and sides, then measure how much was cropped from the top.
sh "pnmcrop -white #{file}.bottomcrop.pnm > #{file}.crop.pnm"
#raise Error, "`pnmcrop` command failed: #{result}" unless ::File.exist?(file + ".crop.pnm")
cropped_img_width, cropped_img_height = pnm_width_height("#{file}.crop.pnm")
topcrop = img_height_bottomcrop - cropped_img_height
# --- Pad image with specific values on all four sides, in preparation for
# downsampling.
# Calculate bottom padding.
snippet_depth = Integer(dimensions["snippetdepth"] + dimensions["pagemargin"] + 0.5) - bottomcrop
padded_snippet_depth = round_up(snippet_depth, oversample)
increase_snippet_depth = padded_snippet_depth - snippet_depth
bottom_padding = increase_snippet_depth
# --- Next calculate top padding, which depends on bottom padding.
padded_img_height = round_up(cropped_img_height + bottom_padding,
oversample)
top_padding = padded_img_height - (cropped_img_height + bottom_padding)
# --- Calculate left and right side padding. Distribute padding evenly.
padded_img_width = round_up(cropped_img_width, oversample)
left_padding = Integer((padded_img_width - cropped_img_width) / 2.0)
right_padding = (padded_img_width - cropped_img_width) - left_padding
# --- Pad the final image.
result = sh "pnmpad",
"-white",
"-bottom=#{bottom_padding}",
"-top=#{top_padding}",
"-left=#{left_padding}",
"-right=#{right_padding}",
"#{file}.crop.pnm",
">#{file}.pad.pnm"
# --- Sanity check of final size.
final_pnm_width, final_pnm_height = pnm_width_height(file + ".pad.pnm")
raise Error, "#{final_pnm_width} is not a multiple of #{oversample}" unless final_pnm_width % oversample == 0
raise "#{final_pnm_height} is not a multiple of #{oversample}" unless final_pnm_height % oversample == 0
# --- Convert PNM to PNG.
final_png_width = final_pnm_width / render_oversample
final_png_height = final_pnm_height / render_oversample
result = sh "cat #{file}.pad.pnm",
"| ppmtopgm",
"| pnmscale -reduce #{render_oversample}",
"| pnmgamma #{gamma}",
"| pnmtopng -compression 9",
"> #{file}.png"
raise Error, "Conversion to png failed: #{result}" unless ::File.exist?(file + ".png")
# Calculate html properties
html_img_width = final_png_width / display_oversample
html_img_height = final_png_height / display_oversample
html_img_vertical_align = sprintf("%.0f", -padded_snippet_depth / oversample)
png_data_base64 = Base64.encode64(::File.open("#{file}.png") { |io| io.read }).chomp
::File.open(cache_file, 'w') { |f| f.write(%{#{html_img_width},#{html_img_height},#{html_img_vertical_align},#{png_data_base64}}) }
if with_properties
return html_img_width, html_img_height, html_img_vertical_align, png_data_base64
else
::File.read(file + ".png")
end
end
end
private
def self.sh_chdir(path, *args)
origcommand = args * " "
return if origcommand == ""
command = origcommand
command.gsub! /(["\\])/, "\\$1"
command = %{/bin/sh -c "(#{command}) 2>&1"}
pid = spawn command, :chdir => path
result = Process::waitpid(pid)
exit_value = Integer($? >> 8), signal_num = Integer($? & 127), dumped_core = Integer($? & 128)
raise Error, "Failed #{result}: #{origcommand}. Exit value = #{exit_value}. Signal Num = #{signal_num}. Dumped core = #{dumped_core}" unless $?.success?
return result
end
def self.sh(*args)
origcommand = args * " "
return if origcommand == ""
command = origcommand
command.gsub! /(["\\])/, "\\$1"
command = %{/bin/sh -c "(#{command}) 2>&1"}
pid = spawn command
#pid = spawn *args
result = Process::waitpid(pid)
exit_value = $? >> 8, signal_num = $? & 127, dumped_core = $? & 128
raise Error, "Failed #{result}: #{origcommand}. Exit value = #{exit_value}. Signal Num = #{signal_num}. Dumped core = #{dumped_core}" unless $?.success?
return result
end
def self.round_up(num, mod)
num + (num % mod == 0 ? 0 : (mod - (num % mod)))
end
def self.pnm_width_height(filename)
raise Error, "#{filename} is not a .pnm file" if filename !~ /\.pnm$/
width = nil, height = nil
::File.open(filename) do |file|
# Read first line
line = file.gets
begin
line = file.gets # Read next line, skipping comments
end while line && line =~ /^#/
if line =~ /^(\d+)\s+(\d+)$/
width = Integer($1)
height = Integer($2)
else
raise Error, "#{filename}: couldn't read image size"
end
end
raise Error, "#{filename}: couldn't read image size" unless width && height
return width, height
end
end
end
+5 -5
View File
@@ -179,7 +179,7 @@ module Gollum
self.class.history_sanitization
@live_preview = options.fetch(:live_preview, true)
@universal_toc = options.fetch(:universal_toc, false)
@mathjax = options[:mathjax] || true
@mathjax = options[:mathjax] || false
end
# Public: check whether the wiki's git repo exists on the filesystem.
@@ -196,9 +196,9 @@ module Gollum
# dir - The directory String relative to the repo.
#
# Returns a Gollum::Page or nil if no matching page was found.
def page(name, version = @ref, dir = nil)
def page(name, version = @ref, dir = nil, exact = false)
version = @ref if version.nil?
@page_class.new(self).find(name, version, dir)
@page_class.new(self).find(name, version, dir, exact)
end
# Public: Convenience method instead of calling page(name, nil, dir).
@@ -208,8 +208,8 @@ module Gollum
# dir - The directory String relative to the repo.
#
# Returns a Gollum::Page or nil if no matching page was found.
def paged(name, dir = nil, version = @ref)
page(name, version, dir)
def paged(name, dir = nil, exact = false, version = @ref)
page(name, version, dir, exact)
end
# Public: Get the static file for a given name.
+56
View File
@@ -50,6 +50,14 @@ context "Frontend" do
assert_not_equal page_1.version.sha, page_2.version.sha
end
test "edit page with slash" do
page_1 = @wiki.page('A')
post "/edit/A", :content => 'abc', :page => 'A', :path => '/////',
:format => page_1.format, :message => 'def'
follow_redirect!
assert last_response.ok?
end
test "edits page header footer and sidebar" do
commits = @wiki.repo.commits('master').size
page_1 = @wiki.page('A')
@@ -142,10 +150,41 @@ context "Frontend" do
assert last_response.ok?
end
test "page create and edit with dash & page rev" do
page = 'c-d-e'
path = 'a/b/' # path must end with /
post '/create', :content => 'create_msg', :page => page,
:path => path, :format => 'markdown', :message => ''
page_c = @wiki.paged(page, path)
assert_equal 'create_msg', page_c.raw_data
# must clear or create_msg will be returned
@wiki.clear_cache
# post '/edit' fails. post '/edit/' works.
post '/edit/', :content => 'edit_msg',
:page => page, :path => path, :message => ''
page_e = @wiki.paged(page, path)
assert_equal 'edit_msg', page_e.raw_data
@wiki.clear_cache
# test `get %r{/(.+?)/([0-9a-f]{40})} do` in app.rb
get '/' + page_c.escaped_url_path + '/' + page_c.version.to_s
assert last_response.ok?
assert_match /create_msg/, last_response.body
get '/' + page_e.escaped_url_path + '/' + page_e.version.to_s
assert last_response.ok?
assert_match /edit_msg/, last_response.body
end
test "guards against creation of existing page" do
name = "A"
post "/create", :content => 'abc', :page => name,
:format => 'markdown', :message => 'def'
follow_redirect!
assert last_response.ok?
@wiki.clear_cache
@@ -182,6 +221,8 @@ context "Frontend" do
assert last_response.ok?
end
=begin
# Grit is broken.
test "reverts single commit" do
page1 = @wiki.page('B')
@@ -207,6 +248,7 @@ context "Frontend" do
assert_not_equal page1.version.sha, page2.version.sha
assert_equal "INITIAL", page2.raw_data.strip
end
=end
test "cannot revert conflicting commit" do
page1 = @wiki.page('A')
@@ -219,6 +261,20 @@ context "Frontend" do
assert_equal page1.version.sha, page2.version.sha
end
test "redirects from 'base_path' or 'base_path/' to 'base_path/Home'" do
Precious::App.set(:wiki_options, {})
get "/"
assert_match "http://example.org/Home", last_response.headers['Location']
Precious::App.set(:wiki_options, { :base_path => '/wiki' })
get "/"
assert_match "http://example.org/wiki/Home", last_response.headers['Location']
Precious::App.set(:wiki_options, { :base_path => '/wiki/' })
get "/"
assert_match "http://example.org/wiki/Home", last_response.headers['Location']
end
def app
Precious::App
end
+53 -2
View File
@@ -517,6 +517,57 @@ np.array([[2,2],[1,3]],np.float)
assert_not_nil rendered.match(output)
end
#########################################################################
#
# Metadata Blocks
#
#########################################################################
test "metadata blocks" do
content = "a\n\n<!-- ---\ntags: [foo, bar]\n-->\n\nb"
output = "<p>a</p>\n\n<p>b</p>"
result = {'tags'=>['foo','bar']}
index = @wiki.repo.index
index.add("Bilbo-Baggins.md", content)
index.commit("Add metadata")
page = @wiki.page("Bilbo Baggins")
rendered = Gollum::Markup.new(page).render
assert_equal output, rendered
assert_equal result, page.meta_data
end
test "metadata blocks with newline" do
content = "a\n\n<!--\n---\ntags: [foo, bar]\n-->\n\nb"
output = "<p>a</p>\n\n<p>b</p>"
result = {'tags'=>['foo','bar']}
index = @wiki.repo.index
index.add("Bilbo-Baggins.md", content)
index.commit("Add metadata")
page = @wiki.page("Bilbo Baggins")
rendered = Gollum::Markup.new(page).render
assert_equal output, rendered
assert_equal result, page.meta_data
end
test "metadata sanitation" do
content = "a\n\n<!-- ---\nfoo: <script>alert('');</script>\n-->\n\nb"
output = "<p>a</p>\n\n<p>b</p>"
result = {'foo'=>nil}
index = @wiki.repo.index
index.add("Bilbo-Baggins.md", content)
index.commit("Add metadata")
page = @wiki.page("Bilbo Baggins")
rendered = Gollum::Markup.new(page).render
assert_equal output, rendered
assert_equal result, page.meta_data
end
#########################################################################
#
# Various
@@ -619,13 +670,13 @@ end
test "TeX block syntax" do
content = 'a \[ a^2 \] b'
output = "<p>a<imgwidth=\"15\"height=\"16\"style=\"vertical-align:-1px;\"src=\"/_tex.png?type=block&data=YV4y\"alt=\"a^2\"/>b</p>"
output = "<p>a<imgsrc=\"http://www.mathtran.org/cgi-bin/toy/?D=4;tex=a^2\"alt=\"a^2\">b</p>"
compare(content, output, 'md')
end
test "TeX inline syntax" do
content = 'a \( a^2 \) b'
output = "<p>a<imgwidth=\"15\"height=\"16\"style=\"vertical-align:-1px;\"src=\"/_tex.png?type=inline&data=YV4y\"alt=\"a^2\"/>b</p>"
output = "<p>a<imgsrc=\"http://www.mathtran.org/cgi-bin/toy/?D=2;tex=a^2\"alt=\"a^2\">b</p>"
compare(content, output, 'md')
end
+2 -1
View File
@@ -99,7 +99,8 @@ context "Page" do
test "cname" do
assert_equal "Foo", Gollum::Page.cname("Foo")
assert_equal "Foo-Bar", Gollum::Page.cname("Foo Bar")
assert_equal "Foo---Bar", Gollum::Page.cname("Foo / Bar")
# / is now a directory delimiter so it must be preserved
assert_equal "Foo-/-Bar", Gollum::Page.cname("Foo / Bar")
assert_equal "José", Gollum::Page.cname("José")
assert_equal "モルドール", Gollum::Page.cname("モルドール")
end
+11 -2
View File
@@ -19,7 +19,7 @@ context "Precious::Views::Pages" do
test "breadcrumb" do
@page.instance_variable_set("@path", "Mordor/Eye-Of-Sauron/Saruman")
@page.instance_variable_set("@base_url", "/")
@page.instance_variable_set("@base_url", "")
assert_equal '<a href="/pages/">Home</a> / <a href="/pages/Mordor/">Mordor</a> / <a href="/pages/Mordor/Eye-Of-Sauron/">Eye-Of-Sauron</a> / Saruman', @page.breadcrumb
end
@@ -29,9 +29,18 @@ context "Precious::Views::Pages" do
test "files_folders" do
@page.instance_variable_set("@path", "Mordor")
@page.instance_variable_set("@base_url", "/")
@page.instance_variable_set("@base_url", "")
results = [FakeResult.new("Mordor/Eye-Of-Sauron.md"), FakeResult.new("Mordor/Orc/Saruman.md"), FakeResult.new("Mordor/.gitkeep")]
@page.instance_variable_set("@results", results)
assert_equal %{<li><a href="/Mordor/Eye-Of-Sauron" class="file">Eye Of Sauron</a></li>\n<li><a href="/pages/Mordor/Orc/" class="folder">Orc</a></li>}, @page.files_folders
end
test "base url" do
# based on test "files_folders"
@page.instance_variable_set("@path", "Mordor")
@page.instance_variable_set("@base_url", "/wiki")
results = [FakeResult.new("Mordor/Eye-Of-Sauron.md"), FakeResult.new("Mordor/Orc/Saruman.md"), FakeResult.new("Mordor/.gitkeep")]
@page.instance_variable_set("@results", results)
assert_equal %{<li><a href="/wiki/Mordor/Eye-Of-Sauron" class="file">Eye Of Sauron</a></li>\n<li><a href="/wiki/pages/Mordor/Orc/" class="folder">Orc</a></li>}, @page.files_folders
end
end
+22 -1
View File
@@ -15,12 +15,33 @@ context "Unicode Support" do
FileUtils.rm_rf(@path)
end
test "create and read non-latin page" do
test "uri encode" do
c = '한글'
assert_equal '%ED%95%9C%EA%B8%80', encodeURIComponent(c)
assert_equal '%ED%95%9C%EA%B8%80', CGI::escape(c)
end
test "create and read non-latin page with anchor" do
@wiki.write_page("test", :markdown, "# 한글")
page = @wiki.page("test")
assert_equal Gollum::Page, page.class
assert_equal "# 한글", utf8(page.raw_data)
# markup.rb
# #简介
# href.gsub('%', '%25') so the anchor works in Firefox.
# <a href="#%25ED%2595%259C%25EA%25B8%2580" id="%ED%95%9C%EA%B8%80" class="anchor"></a>
doc = Nokogiri::HTML page.formatted_data
h1s = doc / :h1
h1 = h1s.first
anchors = h1 / :a
assert_equal 1, h1s.size
assert_equal 1, anchors.size
assert_equal '#%25ED%2595%259C%25EA%25B8%2580', anchors[0]['href']
assert_equal '%ED%95%9C%EA%B8%80', anchors[0]['id']
assert_equal 'anchor', anchors[0]['class']
assert_equal '', anchors[0].text
end
test "unicode with existing format rules" do