validate-website 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|