validate-website 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +8 -0
- data/Rakefile +1 -1
- data/lib/validate_website/colorful_messages.rb +0 -1
- data/lib/validate_website/core.rb +1 -1
- data/lib/validate_website/option_parser.rb +6 -3
- data/lib/validate_website/validator.rb +37 -21
- data/lib/validate_website/validator_class_methods.rb +17 -0
- data/lib/validate_website/version.rb +1 -1
- data/man/man1/validate-website-static.1 +8 -3
- data/man/man1/validate-website.1 +8 -3
- data/test/data/html5-fail.html +476 -0
- data/test/data/validator.nu-failure.json +1 -1
- data/test/static_test.rb +1 -1
- data/test/validator_test.rb +38 -15
- data/test/webmock_helper.rb +0 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d3196fa0c3271fa922d6435a995279a40099389
|
4
|
+
data.tar.gz: c78180f6adf88588d048082a6431f9061f20f0be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 415f9b8ce3a4579747b22a9b5fddfd3948f1af9b3136529ddfd33c39afcd87099f065c7e40563235881307453b7802035da57ba07c0e87b8c4423289f57839ed
|
7
|
+
data.tar.gz: 15cd19df7a2de8f764685a21e61f77160f52a9573b294639168b9d7844d712d7eab0ab432d579057045143acfb41512c97d2eea3e880d944eac159ede82fec70
|
data/History.md
CHANGED
data/Rakefile
CHANGED
@@ -110,7 +110,7 @@ module ValidateWebsite
|
|
110
110
|
# @param [Regexp] Errors to ignore
|
111
111
|
#
|
112
112
|
def validate(doc, body, url, ignore = nil)
|
113
|
-
validator = Validator.new(doc, body, ignore)
|
113
|
+
validator = Validator.new(doc, body, ignore: ignore)
|
114
114
|
if validator.valid?
|
115
115
|
print color(:success, '.', options[:color]) # rspec style
|
116
116
|
else
|
@@ -1,10 +1,9 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
require 'slop'
|
3
2
|
|
4
3
|
module ValidateWebsite
|
5
4
|
# Internal class for parse command line args
|
6
5
|
class Parser
|
7
|
-
VALID_TYPES =
|
6
|
+
VALID_TYPES = %i(crawl static).freeze
|
8
7
|
|
9
8
|
DEFAULT_OPTIONS = {
|
10
9
|
site: 'http://localhost/',
|
@@ -21,6 +20,7 @@ module ValidateWebsite
|
|
21
20
|
# regex to ignore certain validation errors
|
22
21
|
ignore: nil,
|
23
22
|
color: true,
|
23
|
+
html5_validator: 'tidy',
|
24
24
|
# internal verbose for ValidateWebsite
|
25
25
|
verbose: false
|
26
26
|
}.freeze
|
@@ -51,8 +51,11 @@ module ValidateWebsite
|
|
51
51
|
def self.ignore_html5_options(o)
|
52
52
|
o.regexp('-i', '--ignore',
|
53
53
|
'Validation errors to ignore (ex: "valign|autocorrect")')
|
54
|
+
o.string('-x', '--html5-validator',
|
55
|
+
'Change default html5 validator engine (ex: tidy or nu)',
|
56
|
+
default: DEFAULT_OPTIONS[:html5_validator])
|
54
57
|
o.string('-5', '--html5-validator-service-url',
|
55
|
-
'Change default html5 validator service URL')
|
58
|
+
'Change default html5 validator service URL for "nu" engine')
|
56
59
|
end
|
57
60
|
|
58
61
|
def self.markup_syntax(o)
|
@@ -1,27 +1,24 @@
|
|
1
1
|
require 'uri'
|
2
|
+
|
2
3
|
require 'nokogiri'
|
3
4
|
require 'w3c_validators'
|
4
5
|
|
6
|
+
require 'validate_website/validator_class_methods'
|
7
|
+
|
5
8
|
module ValidateWebsite
|
6
9
|
# Document validation from DTD or XSD (webservice for html5)
|
7
10
|
class Validator
|
8
|
-
|
11
|
+
extend ValidatorClassMethods
|
9
12
|
|
13
|
+
@html5_validator_service_url = 'https://checker.html5.org/'
|
10
14
|
class << self
|
11
15
|
attr_accessor :html5_validator_service_url
|
12
|
-
|
13
|
-
def validator_uri
|
14
|
-
@validator_uri ||=
|
15
|
-
ENV['VALIDATOR_NU_URL'] || @html5_validator_service_url
|
16
|
-
end
|
17
16
|
end
|
18
17
|
|
19
18
|
XHTML_PATH = File.expand_path('../../../data/schemas', __FILE__)
|
20
19
|
|
21
|
-
@xsd_schemas
|
22
|
-
|
23
|
-
attr_reader :xsd_schemas
|
24
|
-
end
|
20
|
+
@xsd_schemas ||= {}
|
21
|
+
|
25
22
|
# `Dir.chdir` is needed by `Nokogiri::XML::Schema` to validate with local
|
26
23
|
# files and cannot use file absolute path.
|
27
24
|
Dir.glob(File.join(XHTML_PATH, '*.xsd')).each do |schema|
|
@@ -36,19 +33,21 @@ module ValidateWebsite
|
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
39
|
-
attr_reader :original_doc, :body, :dtd, :doc, :namespace
|
36
|
+
attr_reader :original_doc, :body, :dtd, :doc, :namespace, :html5_validator
|
40
37
|
|
41
38
|
##
|
42
39
|
# @param [Nokogiri::HTML::Document] original_doc
|
43
40
|
# @param [String] The raw HTTP response body of the page
|
44
41
|
# @param [Regexp] Errors to ignore
|
45
|
-
#
|
46
|
-
|
42
|
+
# @param [Symbol] html5_validator default offline :tidy
|
43
|
+
# fallback webservice :nu
|
44
|
+
def initialize(original_doc, body, ignore: nil, html5_validator: :tidy)
|
47
45
|
@errors = []
|
48
46
|
@document, @dtd_uri = nil
|
49
47
|
@original_doc = original_doc
|
50
48
|
@body = body
|
51
49
|
@ignore = ignore
|
50
|
+
@html5_validator = html5_validator
|
52
51
|
@dtd = @original_doc.internal_subset
|
53
52
|
@namespace = find_namespace(@dtd)
|
54
53
|
end
|
@@ -93,11 +92,11 @@ module ValidateWebsite
|
|
93
92
|
end
|
94
93
|
|
95
94
|
# @return [Array] contain result errors
|
96
|
-
def validate(
|
95
|
+
def validate(xhtml_doc)
|
97
96
|
if self.class.xsd(@namespace)
|
98
|
-
self.class.xsd(@namespace).validate(
|
99
|
-
elsif
|
100
|
-
html5_validate
|
97
|
+
self.class.xsd(@namespace).validate(xhtml_doc)
|
98
|
+
elsif document =~ /^\<!DOCTYPE html\>/i
|
99
|
+
html5_validate
|
101
100
|
else
|
102
101
|
# dont have xsd fall back to dtd
|
103
102
|
Dir.chdir(XHTML_PATH) do
|
@@ -108,20 +107,37 @@ module ValidateWebsite
|
|
108
107
|
|
109
108
|
# http://nokogiri.org/tutorials/ensuring_well_formed_markup.html
|
110
109
|
def find_errors
|
111
|
-
|
110
|
+
xhtml_doc = Dir.chdir(XHTML_PATH) do
|
112
111
|
Nokogiri::XML(document) { |cfg| cfg.noent.dtdload.dtdvalid }
|
113
112
|
end
|
114
|
-
@errors = validate(
|
113
|
+
@errors = validate(xhtml_doc)
|
115
114
|
rescue Nokogiri::XML::SyntaxError => e
|
116
115
|
@errors << e
|
117
116
|
end
|
118
117
|
|
119
|
-
def html5_validate
|
118
|
+
def html5_validate
|
119
|
+
if html5_validator.to_sym == :tidy && self.class.tidy
|
120
|
+
tidy_validate
|
121
|
+
else
|
122
|
+
nu_validate
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def tidy_validate
|
127
|
+
results = self.class.tidy.new(document)
|
128
|
+
if results.errors
|
129
|
+
errors.concat(results.errors.split("\n"))
|
130
|
+
else
|
131
|
+
[]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def nu_validate
|
120
136
|
validator = W3CValidators::NuValidator.new(
|
121
137
|
validator_uri: self.class.validator_uri
|
122
138
|
)
|
123
139
|
results = validator.validate_text(document)
|
124
|
-
errors.concat
|
140
|
+
errors.concat(results.errors)
|
125
141
|
end
|
126
142
|
end
|
127
143
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'tidy_ffi'
|
2
|
+
|
3
|
+
# Validator Class Methods
|
4
|
+
module ValidatorClassMethods
|
5
|
+
def validator_uri
|
6
|
+
@validator_uri ||=
|
7
|
+
ENV['VALIDATOR_NU_URL'] || @html5_validator_service_url
|
8
|
+
end
|
9
|
+
|
10
|
+
def tidy
|
11
|
+
return @tidy if defined?(@tidy)
|
12
|
+
@lib_tidy = TidyFFI::LibTidy
|
13
|
+
@tidy = TidyFFI::Tidy
|
14
|
+
rescue TidyFFI::LibTidyNotInstalled
|
15
|
+
@tidy = nil
|
16
|
+
end
|
17
|
+
end
|
@@ -2,12 +2,12 @@
|
|
2
2
|
.\" Title: validate-website-static
|
3
3
|
.\" Author: [see the "AUTHOR" section]
|
4
4
|
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
5
|
-
.\" Date:
|
5
|
+
.\" Date: 06/20/2017
|
6
6
|
.\" Manual: \ \&
|
7
7
|
.\" Source: \ \&
|
8
8
|
.\" Language: English
|
9
9
|
.\"
|
10
|
-
.TH "VALIDATE\-WEBSITE\-S" "1" "
|
10
|
+
.TH "VALIDATE\-WEBSITE\-S" "1" "06/20/2017" "\ \&" "\ \&"
|
11
11
|
.\" -----------------------------------------------------------------
|
12
12
|
.\" * Define some portability stuff
|
13
13
|
.\" -----------------------------------------------------------------
|
@@ -80,9 +80,14 @@ Log files not on filesystem, pwd considered as root \(Fo / \(Fc (Default: false)
|
|
80
80
|
Show colored output (Default: true)
|
81
81
|
.RE
|
82
82
|
.PP
|
83
|
+
\fB\-x\fR, \fB\-\-html5\-validator\fR \fIVALIDATOR\fR
|
84
|
+
.RS 4
|
85
|
+
Change default html5 validator engine (ex: tidy or nu)
|
86
|
+
.RE
|
87
|
+
.PP
|
83
88
|
\fB\-5\fR, \fB\-\-html5\-validator\-service\-url\fR \fIURL\fR
|
84
89
|
.RS 4
|
85
|
-
Change default html5 validator service URL
|
90
|
+
Change default html5 validator service URL for "nu" engine
|
86
91
|
.RE
|
87
92
|
.PP
|
88
93
|
\fB\-v\fR, \fB\-\-verbose\fR
|
data/man/man1/validate-website.1
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
.\" Title: validate-website
|
3
3
|
.\" Author: [see the "AUTHOR" section]
|
4
4
|
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
5
|
-
.\" Date:
|
5
|
+
.\" Date: 06/20/2017
|
6
6
|
.\" Manual: \ \&
|
7
7
|
.\" Source: \ \&
|
8
8
|
.\" Language: English
|
9
9
|
.\"
|
10
|
-
.TH "VALIDATE\-WEBSITE" "1" "
|
10
|
+
.TH "VALIDATE\-WEBSITE" "1" "06/20/2017" "\ \&" "\ \&"
|
11
11
|
.\" -----------------------------------------------------------------
|
12
12
|
.\" * Define some portability stuff
|
13
13
|
.\" -----------------------------------------------------------------
|
@@ -85,9 +85,14 @@ Log not found url (Default: false)
|
|
85
85
|
Show colored output (Default: true)
|
86
86
|
.RE
|
87
87
|
.PP
|
88
|
+
\fB\-x\fR, \fB\-\-html5\-validator\fR \fIVALIDATOR\fR
|
89
|
+
.RS 4
|
90
|
+
Change default html5 validator engine (ex: tidy or nu)
|
91
|
+
.RE
|
92
|
+
.PP
|
88
93
|
\fB\-5\fR, \fB\-\-html5\-validator\-service\-url\fR \fIURL\fR
|
89
94
|
.RS 4
|
90
|
-
Change default html5 validator service URL
|
95
|
+
Change default html5 validator service URL for "nu" engine
|
91
96
|
.RE
|
92
97
|
.PP
|
93
98
|
\fB\-v\fR, \fB\-\-verbose\fR
|
@@ -0,0 +1,476 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<!-- Page last generated 2017-04-08 21:20:41 +0000 -->
|
3
|
+
<html lang="en">
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
8
|
+
<title>The Rust Programming Language</title>
|
9
|
+
<meta name="keywords" content="Rust, Rust programming language, rustlang, rust-lang, Mozilla Rust">
|
10
|
+
<meta name="description" content="A systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.">
|
11
|
+
|
12
|
+
<link rel="stylesheet" href="/css/bootstrap.css">
|
13
|
+
<link rel="stylesheet" href="/css/style.css">
|
14
|
+
|
15
|
+
</head>
|
16
|
+
|
17
|
+
<body class="container">
|
18
|
+
<a href="https://github.com/rust-lang/rust">
|
19
|
+
<img class="ribbon" style="display: none" src="/logos/forkme.png" alt="Fork me on GitHub" width="298" height="298">
|
20
|
+
</a>
|
21
|
+
|
22
|
+
<header>
|
23
|
+
<ul class="row menu">
|
24
|
+
<li class="col-xs-12 col-md-2">
|
25
|
+
<a href="/en-US/index.html">
|
26
|
+
<img class="img-responsive" src="/logos/rust-logo-blk.svg" onerror="this.src='/logos/rust-logo-256x256-blk.png'" height="128" width="128" alt="Rust logo" />
|
27
|
+
</a>
|
28
|
+
</li>
|
29
|
+
<li class="col-xs-12 col-md-10 menu">
|
30
|
+
<h2><a href="/en-US/documentation.html">Documentation</a></h2>
|
31
|
+
<h2><a href="/en-US/install.html">Install</a></h2>
|
32
|
+
<h2><a href="/en-US/community.html">Community</a></h2>
|
33
|
+
<h2><a href="/en-US/contribute.html">Contribute</a></h2>
|
34
|
+
</li>
|
35
|
+
</ul>
|
36
|
+
</header>
|
37
|
+
|
38
|
+
<div class="row pitch-row">
|
39
|
+
<div class="col-md-8">
|
40
|
+
<p class="pitch">
|
41
|
+
<b>Rust</b> is a systems programming language
|
42
|
+
that runs blazingly fast,
|
43
|
+
prevents segfaults,
|
44
|
+
and guarantees thread safety.
|
45
|
+
<br/>
|
46
|
+
<b><a href="friends.html">See who's using Rust.</a></b>
|
47
|
+
</p>
|
48
|
+
</div>
|
49
|
+
<div class="col-md-4">
|
50
|
+
<a class="release-button" href="install.html">
|
51
|
+
<div class="release-version">Install Rust <span>1.16.0</span></div>
|
52
|
+
</a>
|
53
|
+
<div class="release-date">March 16, 2017</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<div class="row code-row">
|
58
|
+
<div class="col-md-4">
|
59
|
+
<h2>Featuring</h2>
|
60
|
+
<ul class="laundry-list">
|
61
|
+
<li>zero-cost abstractions</li>
|
62
|
+
<li>move semantics</li>
|
63
|
+
<li>guaranteed memory safety</li>
|
64
|
+
<li>threads without data races</li>
|
65
|
+
<li>trait-based generics</li>
|
66
|
+
<li>pattern matching</li>
|
67
|
+
<li>type inference</li>
|
68
|
+
<li>minimal runtime</li>
|
69
|
+
<li>efficient C bindings</li>
|
70
|
+
</ul>
|
71
|
+
</div>
|
72
|
+
<div class="col-md-8">
|
73
|
+
<div id="active-code">
|
74
|
+
<button type="button" class="btn btn-primary btn-sm" id="run-code">Run</button>
|
75
|
+
<div id="editor">fn main() {
|
76
|
+
let greetings = ["Hello", "Hola", "Bonjour",
|
77
|
+
"こんにちは", "您好"];
|
78
|
+
|
79
|
+
for (num, greeting) in greetings.iter().enumerate() {
|
80
|
+
println!("{}", greeting);
|
81
|
+
match num {
|
82
|
+
0 => println!("This code is editable and runnable!"),
|
83
|
+
1 => println!("Este código es editable y ejecutable!"),
|
84
|
+
2 => println!("Ce code est modifiable et exécutable!"),
|
85
|
+
3 => println!("このコードは編集して実行出来ます!"),
|
86
|
+
4 => println!("这个代码是可以编辑并且能够运行的!"),
|
87
|
+
_ => {},
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
</div>
|
92
|
+
<div id="result" data-msg-running="Running...">
|
93
|
+
<a id="playlink"><i class="icon-link-ext"></i></a>
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
<div id="static-code"><pre class='rust'>
|
97
|
+
<span class='kw'>fn</span> main() {
|
98
|
+
<span class='kw'>let</span> greetings = [<span class='string'>"Hello"</span>, <span class='string'>"Hola"<span>, <span class='string'>"Bonjour"</span>,
|
99
|
+
<span class='string'>"こんにちは"</span>, <span class='string'>"您好"</span>];
|
100
|
+
|
101
|
+
<span class='kw'>for</span> (num, greeting) in greetings.iter().enumerate() {
|
102
|
+
<span class='prelude-val'>println!</span>(<span class='string'>"{}"</span>, greeting);
|
103
|
+
<span class='kw'>match</span> num {
|
104
|
+
0 => <span class='prelude-val'>println!</span>(<span class='string'>"This code is editable and runnable!"</span>),
|
105
|
+
1 => <span class='prelude-val'>println!</span>(<span class='string'>"Este código es editable y ejecutable!"</span>),
|
106
|
+
2 => <span class='prelude-val'>println!</span>(<span class='string'>"Ce code est modifiable et exécutable!"</span>),
|
107
|
+
3 => <span class='prelude-val'>println!</span>(<span class='string'>"このコードは編集して実行出来ます!"</span>),
|
108
|
+
4 => <span class='prelude-val'>println!</span>(<span class='string'>"这个代码是可以编辑并且能够运行的!"</span>),
|
109
|
+
_ => {},
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
</pre>
|
114
|
+
</div>
|
115
|
+
<div class="more-examples">
|
116
|
+
<a href="http://rustbyexample.com/">More examples</a>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
|
121
|
+
<script type="text/javascript">
|
122
|
+
include = function() {
|
123
|
+
|
124
|
+
// save references to save a few bytes
|
125
|
+
var args = arguments;
|
126
|
+
var doc = document;
|
127
|
+
|
128
|
+
var toLoad = args.length; // load this many scripts
|
129
|
+
var lastArgument = args[toLoad - 1];
|
130
|
+
var hasCallback = lastArgument.call; // is the last arg a callback?
|
131
|
+
if (hasCallback) {
|
132
|
+
toLoad--;
|
133
|
+
}
|
134
|
+
|
135
|
+
function onScriptLoaded() {
|
136
|
+
var readyState = this.readyState; // we test for "complete" or "loaded" if on IE
|
137
|
+
if (!readyState || /ded|te/.test(readyState)) {
|
138
|
+
toLoad--;
|
139
|
+
if (!toLoad && hasCallback) {
|
140
|
+
lastArgument();
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
for (var i = 0; i < toLoad; i++) {
|
146
|
+
|
147
|
+
var script = doc.createElement('script');
|
148
|
+
script.src = arguments[i];
|
149
|
+
script.async = true;
|
150
|
+
script.onload = script.onerror = script.onreadystatechange = onScriptLoaded;
|
151
|
+
(
|
152
|
+
doc.head ||
|
153
|
+
doc.getElementsByTagName('head')[0]
|
154
|
+
).appendChild(script);
|
155
|
+
|
156
|
+
}
|
157
|
+
|
158
|
+
};
|
159
|
+
|
160
|
+
|
161
|
+
include("https://cdn.jsdelivr.net/ace/1.1.3/noconflict/ace.js", function () {
|
162
|
+
include("https://cdn.jsdelivr.net/ace/1.1.3/noconflict/mode-rust.js", function () {
|
163
|
+
(function () {
|
164
|
+
"use strict";
|
165
|
+
// ECMAScript 6 Backwards compatability
|
166
|
+
if (typeof String.prototype.startsWith !== 'function') {
|
167
|
+
String.prototype.startsWith = function(str) {
|
168
|
+
return this.slice(0, str.length) === str;
|
169
|
+
};
|
170
|
+
}
|
171
|
+
|
172
|
+
// Regex for finding new lines
|
173
|
+
var newLineRegex = /(?:\r\n|\r|\n)/g;
|
174
|
+
|
175
|
+
// Fetching DOM items
|
176
|
+
var activeCode = document.getElementById("active-code");
|
177
|
+
var editorDiv = document.getElementById("editor");
|
178
|
+
var staticCode = document.getElementById("static-code");
|
179
|
+
var runButton = document.getElementById("run-code");
|
180
|
+
var resultDiv = document.getElementById("result");
|
181
|
+
var playLink = document.getElementById("playlink");
|
182
|
+
|
183
|
+
// Background colors for program result on success/error
|
184
|
+
var successColor = "#E2EEF6";
|
185
|
+
var errorColor = "#F6E2E2";
|
186
|
+
var warningColor = "#FFFBCB";
|
187
|
+
|
188
|
+
// Message to show when the program is running
|
189
|
+
var runningMsg = resultDiv.getAttribute("data-msg-running") || "Running...";
|
190
|
+
|
191
|
+
// Error message to return when there's a server failure
|
192
|
+
var errMsg = "The server encountered an error while running the program.";
|
193
|
+
|
194
|
+
// Stores ACE editor markers (highights) for errors
|
195
|
+
var markers = [];
|
196
|
+
|
197
|
+
// Status codes, because there are no enums in Javascript
|
198
|
+
var SUCCESS = 0;
|
199
|
+
var ERROR = 1;
|
200
|
+
var WARNING = 2;
|
201
|
+
|
202
|
+
// JS exists, display ACE editor
|
203
|
+
staticCode.style.display = "none";
|
204
|
+
activeCode.style.display = "block";
|
205
|
+
|
206
|
+
// Setting up ace editor
|
207
|
+
var editor = ace.edit("editor");
|
208
|
+
var Range = ace.require('ace/range').Range;
|
209
|
+
editor.setTheme("ace/theme/chrome");
|
210
|
+
editor.getSession().setMode("ace/mode/rust");
|
211
|
+
editor.setShowPrintMargin(false);
|
212
|
+
editor.renderer.setShowGutter(false);
|
213
|
+
editor.setHighlightActiveLine(false);
|
214
|
+
|
215
|
+
// Changes the height of the editor to match its contents
|
216
|
+
function updateEditorHeight() {
|
217
|
+
// https://stackoverflow.com/questions/11584061/
|
218
|
+
var newHeight = editor.getSession().getScreenLength()
|
219
|
+
* editor.renderer.lineHeight
|
220
|
+
+ editor.renderer.scrollBar.getWidth();
|
221
|
+
|
222
|
+
editorDiv.style.height = Math.ceil(newHeight).toString() + "px";
|
223
|
+
editor.resize();
|
224
|
+
}
|
225
|
+
|
226
|
+
// Set initial size to match initial content
|
227
|
+
updateEditorHeight();
|
228
|
+
|
229
|
+
// Safely remove all content from the result div
|
230
|
+
function clearResultDiv() {
|
231
|
+
// Clearing the result div will break our reference to
|
232
|
+
// the playlink icon, so let's save it if it exists
|
233
|
+
var newPlayLink = document.getElementById("playlink");
|
234
|
+
if (newPlayLink) {
|
235
|
+
playLink = resultDiv.removeChild(newPlayLink);
|
236
|
+
}
|
237
|
+
resultDiv.innerHTML = "";
|
238
|
+
}
|
239
|
+
|
240
|
+
function escapeHTML(unsafe) {
|
241
|
+
return unsafe
|
242
|
+
.replace(/&/g, "&")
|
243
|
+
.replace(/</g, "<")
|
244
|
+
.replace(/>/g, ">")
|
245
|
+
.replace(/"/g, """)
|
246
|
+
.replace(/'/g, "'")
|
247
|
+
.replace(newLineRegex, '<br />');
|
248
|
+
}
|
249
|
+
|
250
|
+
// Dispatches a XMLHttpRequest to the Rust playpen, running the program, and
|
251
|
+
// issues a callback to `callback` with the result (or null on error)
|
252
|
+
function runProgram(program, callback) {
|
253
|
+
var req = new XMLHttpRequest();
|
254
|
+
var data = JSON.stringify({
|
255
|
+
version: "beta",
|
256
|
+
optimize: "0",
|
257
|
+
code: program
|
258
|
+
});
|
259
|
+
|
260
|
+
req.timeout = 6000;
|
261
|
+
|
262
|
+
// console.log("Sending", data);
|
263
|
+
req.open('POST', "https://play.rust-lang.org/evaluate.json", true);
|
264
|
+
req.onload = function(e) {
|
265
|
+
var statusCode = false;
|
266
|
+
var result = null;
|
267
|
+
|
268
|
+
if (req.readyState === 4 && req.status === 200) {
|
269
|
+
result = JSON.parse(req.response);
|
270
|
+
|
271
|
+
// handle application errors from playpen
|
272
|
+
if (typeof result['error'] === 'string') {
|
273
|
+
statusCode = ERROR;
|
274
|
+
result = 'Playpen Error: ' + result['error'];
|
275
|
+
} else if (typeof result['result'] === 'string') {
|
276
|
+
statusCode = SUCCESS;
|
277
|
+
result = result['result'];
|
278
|
+
|
279
|
+
// handle rustc errors/warnings
|
280
|
+
// Need server support to get an accurate version of this.
|
281
|
+
if (result.indexOf("error:") !== -1) {
|
282
|
+
statusCode = ERROR;
|
283
|
+
} else if (result.indexOf("warning:") !== -1) {
|
284
|
+
statusCode = WARNING;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
callback(statusCode, result);
|
290
|
+
};
|
291
|
+
|
292
|
+
req.onerror = function(e) {
|
293
|
+
callback(false, null);
|
294
|
+
};
|
295
|
+
|
296
|
+
req.ontimeout = function(e) {
|
297
|
+
var statusCode = ERROR;
|
298
|
+
var result = "play.rust-lang.org not responding, please check back later";
|
299
|
+
|
300
|
+
callback(statusCode, result);
|
301
|
+
}
|
302
|
+
|
303
|
+
req.setRequestHeader("Content-Type", "application/json");
|
304
|
+
req.send(data);
|
305
|
+
}
|
306
|
+
|
307
|
+
// The callback to runProgram
|
308
|
+
function handleResult(statusCode, message) {
|
309
|
+
// Dispatch depending on result type
|
310
|
+
if (result == null) {
|
311
|
+
clearResultDiv();
|
312
|
+
resultDiv.style.backgroundColor = errorColor;
|
313
|
+
resultDiv.innerHTML = errMsg;
|
314
|
+
} else if (statusCode === SUCCESS) {
|
315
|
+
handleSuccess(message);
|
316
|
+
} else if (statusCode === WARNING) {
|
317
|
+
handleWarning(message);
|
318
|
+
} else {
|
319
|
+
handleError(message);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
// Called on successful program run: display output and playground icon
|
324
|
+
function handleSuccess(message) {
|
325
|
+
resultDiv.style.backgroundColor = successColor;
|
326
|
+
displayOutput(escapeHTML(message), editor.getValue());
|
327
|
+
}
|
328
|
+
|
329
|
+
// Called when program run results in warning(s)
|
330
|
+
function handleWarning(message) {
|
331
|
+
resultDiv.style.backgroundColor = warningColor;
|
332
|
+
handleProblem(message, "warning");
|
333
|
+
}
|
334
|
+
|
335
|
+
// Called when program run results in error(s)
|
336
|
+
function handleError(message) {
|
337
|
+
resultDiv.style.backgroundColor = errorColor;
|
338
|
+
handleProblem(message, "error");
|
339
|
+
}
|
340
|
+
|
341
|
+
// Called on unsuccessful program run. Detects and prints problems (either
|
342
|
+
// warnings or errors) in program output and highlights relevant lines and text
|
343
|
+
// in the code.
|
344
|
+
function handleProblem(message, problem) {
|
345
|
+
// Getting list of ranges with problems
|
346
|
+
var lines = message.split(newLineRegex);
|
347
|
+
|
348
|
+
// Cleaning up the message: keeps only relevant problem output.
|
349
|
+
var cleanMessage = lines.filter(function(line) {
|
350
|
+
return !line.trim().startsWith("--> <anon>")
|
351
|
+
&& !line.startsWith("playpen:")
|
352
|
+
&& !line.trim().startsWith("error: aborting");
|
353
|
+
}).map(function(line) {
|
354
|
+
return escapeHTML(line);
|
355
|
+
}).filter(function(line) {
|
356
|
+
return line != "";
|
357
|
+
}).map(function(line) {
|
358
|
+
return line.replace(/ /g, '\u00a0\u00a0');
|
359
|
+
}).join("<br />");
|
360
|
+
|
361
|
+
// Get all of the row:col in the message.
|
362
|
+
var errorLines = lines.filter(function(line) {
|
363
|
+
return line.indexOf("--> <anon>") !== -1;
|
364
|
+
}).map(function(line) {
|
365
|
+
var lineIndex = line.indexOf(":");
|
366
|
+
if (lineIndex !== -1) {
|
367
|
+
return line.slice(lineIndex);
|
368
|
+
}
|
369
|
+
|
370
|
+
return "";
|
371
|
+
}).filter(function(line) {
|
372
|
+
return line != "";
|
373
|
+
});
|
374
|
+
|
375
|
+
// Setting message
|
376
|
+
displayOutput(cleanMessage, editor.getValue());
|
377
|
+
|
378
|
+
// Highlighting the lines
|
379
|
+
var ranges = parseProblems(errorLines);
|
380
|
+
markers = ranges.map(function(range) {
|
381
|
+
return editor.getSession().addMarker(range, "ace-" + problem + "-line",
|
382
|
+
"fullLine", false);
|
383
|
+
});
|
384
|
+
|
385
|
+
// Highlighting the specific text
|
386
|
+
markers = markers.concat(ranges.map(function(range) {
|
387
|
+
return editor.getSession().addMarker(range, "ace-" + problem + "-text",
|
388
|
+
"text", false);
|
389
|
+
}));
|
390
|
+
}
|
391
|
+
|
392
|
+
// Parses a problem message returning a list of ranges (row:col, row:col) where
|
393
|
+
// problems in the code have occured.
|
394
|
+
function parseProblems(lines) {
|
395
|
+
var ranges = [];
|
396
|
+
for (var i in lines) {
|
397
|
+
var line = lines[i];
|
398
|
+
var parts = line.split(/:\s?|\s+/, 5).slice(1, 5);
|
399
|
+
var ip = parts.map(function(p) { return parseInt(p, 10) - 1; });
|
400
|
+
console.log("line:", line, parts, ip);
|
401
|
+
ranges.push(new Range(ip[0], ip[1], ip[2], ip[3]));
|
402
|
+
}
|
403
|
+
|
404
|
+
return ranges;
|
405
|
+
}
|
406
|
+
|
407
|
+
// Registering handler for run button click
|
408
|
+
runButton.addEventListener("click", function(ev) {
|
409
|
+
resultDiv.style.display = "block";
|
410
|
+
clearResultDiv();
|
411
|
+
resultDiv.innerHTML = runningMsg;
|
412
|
+
|
413
|
+
// clear previous markers, if any
|
414
|
+
markers.map(function(id) { editor.getSession().removeMarker(id); });
|
415
|
+
|
416
|
+
// Get the code, run the program
|
417
|
+
var program = editor.getValue();
|
418
|
+
runProgram(program, handleResult);
|
419
|
+
});
|
420
|
+
|
421
|
+
// Display an output message and a link to the Rust playground
|
422
|
+
function displayOutput(message, program) {
|
423
|
+
var programUrl = "https://play.rust-lang.org/?code=" +
|
424
|
+
encodeURIComponent(program) + "&run=1";
|
425
|
+
playLink.href = programUrl;
|
426
|
+
|
427
|
+
clearResultDiv(); // clear resultDiv, then add
|
428
|
+
resultDiv.appendChild(playLink); // playLink icon and message
|
429
|
+
resultDiv.innerHTML += message;
|
430
|
+
}
|
431
|
+
|
432
|
+
// Highlight active line when focused
|
433
|
+
editor.on('focus', function() {
|
434
|
+
editor.setHighlightActiveLine(true);
|
435
|
+
});
|
436
|
+
|
437
|
+
// Don't when not
|
438
|
+
editor.on('blur', function() {
|
439
|
+
editor.setHighlightActiveLine(false);
|
440
|
+
});
|
441
|
+
}());
|
442
|
+
|
443
|
+
});
|
444
|
+
});
|
445
|
+
</script>
|
446
|
+
|
447
|
+
|
448
|
+
<footer>
|
449
|
+
<p>Our site in other languages:
|
450
|
+
<a href="/en-US/">English</a>,
|
451
|
+
<a href="/es-ES/">Español</a>,
|
452
|
+
<a href="/fr-FR/">Français</a>,
|
453
|
+
<a href="/it-IT/">Italiano</a>,
|
454
|
+
<a href="/ja-JP/">日本語</a>,
|
455
|
+
<a href="/ko-KR/">한국어</a>,
|
456
|
+
<a href="/pl-PL/">Polski</a>,
|
457
|
+
<a href="/pt-BR/">Português</a>,
|
458
|
+
<a href="/ru-RU/">Русский</a>,
|
459
|
+
<a href="/vi-VN/">Tiếng việt</a>,
|
460
|
+
<a href="/zh-CN/">简体中文</a>
|
461
|
+
|
462
|
+
</p>
|
463
|
+
</footer>
|
464
|
+
|
465
|
+
<script>
|
466
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
467
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
468
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
469
|
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
470
|
+
|
471
|
+
ga('create', 'UA-58390457-1', 'auto');
|
472
|
+
ga('send', 'pageview');
|
473
|
+
|
474
|
+
</script>
|
475
|
+
</body>
|
476
|
+
</html>
|
@@ -1 +1 @@
|
|
1
|
-
{"url":"https://
|
1
|
+
{"url":"https://www.rust-lang.org/en-US/","messages":[{"type":"error","lastLine":113,"lastColumn":6,"firstColumn":1,"message":"End tag “pre” seen, but there were open elements.","extract":"}\n }\n}\n</pre>\n</div","hiliteStart":10,"hiliteLength":6},{"type":"error","lastLine":98,"lastColumn":115,"firstColumn":110,"message":"Unclosed element “span”.","extract":"ng'>\"Hola\"<span>, <spa","hiliteStart":10,"hiliteLength":6},{"type":"error","lastLine":98,"lastColumn":103,"firstColumn":83,"message":"Unclosed element “span”.","extract":"\"</span>, <span class='string'>\"Hola\"","hiliteStart":10,"hiliteLength":21}]}
|
data/test/static_test.rb
CHANGED
data/test/validator_test.rb
CHANGED
@@ -19,7 +19,7 @@ describe ValidateWebsite::Validator do
|
|
19
19
|
ignore = /width|height|Length/
|
20
20
|
validator = subject.new(@xhtml1_page.doc,
|
21
21
|
@xhtml1_page.body,
|
22
|
-
ignore)
|
22
|
+
ignore: ignore)
|
23
23
|
validator.valid?.must_equal true
|
24
24
|
validator.errors.must_equal []
|
25
25
|
end
|
@@ -35,7 +35,7 @@ describe ValidateWebsite::Validator do
|
|
35
35
|
ignore = /width|height|Length/
|
36
36
|
validator = subject.new(@xhtml1_page.doc,
|
37
37
|
@xhtml1_page.body,
|
38
|
-
ignore)
|
38
|
+
ignore: ignore)
|
39
39
|
validator.dtd.system_id.must_equal dtd_uri
|
40
40
|
validator.namespace.must_equal name
|
41
41
|
validator.valid?.must_equal true
|
@@ -62,12 +62,13 @@ describe ValidateWebsite::Validator do
|
|
62
62
|
validator.valid?.must_equal true
|
63
63
|
end
|
64
64
|
end
|
65
|
+
|
65
66
|
describe('when not valid') do
|
66
67
|
before do
|
67
68
|
validator_res = File.join('test', 'data', 'validator.nu-failure.json')
|
68
69
|
stub_request(:any, /#{subject.html5_validator_service_url}/)
|
69
70
|
.to_return(body: open(validator_res).read)
|
70
|
-
name = 'html5'
|
71
|
+
name = 'html5-fail'
|
71
72
|
file = File.join('test', 'data', "#{name}.html")
|
72
73
|
page = FakePage.new(name,
|
73
74
|
body: open(file).read,
|
@@ -75,20 +76,42 @@ describe ValidateWebsite::Validator do
|
|
75
76
|
@html5_page = @http.get_page(page.url)
|
76
77
|
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
79
|
+
describe('without tidy') do
|
80
|
+
it 'should have an array of errors' do
|
81
|
+
validator = subject.new(@html5_page.doc,
|
82
|
+
@html5_page.body,
|
83
|
+
html5_validator: :nu)
|
84
|
+
validator.valid?.must_equal false
|
85
|
+
validator.errors.size.must_equal 3
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should exclude errors ignored by :ignore option' do
|
89
|
+
ignore = /Unclosed element/
|
90
|
+
validator = subject.new(@html5_page.doc,
|
91
|
+
@html5_page.body,
|
92
|
+
ignore: ignore,
|
93
|
+
html5_validator: :nu)
|
94
|
+
validator.valid?.must_equal false
|
95
|
+
validator.errors.size.must_equal 1
|
96
|
+
end
|
83
97
|
end
|
84
98
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
99
|
+
describe('with tidy') do
|
100
|
+
it 'should have an array of errors' do
|
101
|
+
validator = subject.new(@html5_page.doc,
|
102
|
+
@html5_page.body)
|
103
|
+
validator.valid?.must_equal false
|
104
|
+
validator.errors.size.must_equal 4
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should exclude errors ignored by :ignore option' do
|
108
|
+
ignore = /letter not allowed here|trimming empty/
|
109
|
+
validator = subject.new(@html5_page.doc,
|
110
|
+
@html5_page.body,
|
111
|
+
ignore: ignore)
|
112
|
+
validator.valid?.must_equal false
|
113
|
+
validator.errors.size.must_equal 2
|
114
|
+
end
|
92
115
|
end
|
93
116
|
end
|
94
117
|
end
|
data/test/webmock_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validate-website
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Laurent Arnoud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: spidr
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tidy_ffi
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: slop
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,6 +212,7 @@ files:
|
|
198
212
|
- lib/validate_website/static_link.rb
|
199
213
|
- lib/validate_website/utils.rb
|
200
214
|
- lib/validate_website/validator.rb
|
215
|
+
- lib/validate_website/validator_class_methods.rb
|
201
216
|
- lib/validate_website/version.rb
|
202
217
|
- man/man1/validate-website-static.1
|
203
218
|
- man/man1/validate-website.1
|
@@ -206,6 +221,7 @@ files:
|
|
206
221
|
- test/data/assets/application-92f19110a9d47a56d2ebe744e15af301.css
|
207
222
|
- test/data/cozy-community.html
|
208
223
|
- test/data/html4-strict.html
|
224
|
+
- test/data/html5-fail.html
|
209
225
|
- test/data/html5.html
|
210
226
|
- test/data/news/ryzom-naissance-du-projet-libre-ryzom-forge.md
|
211
227
|
- test/data/validator.nu-failure.json
|