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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba01cfb0d05bb3670aecfb1afcc94bc72dd02088
4
- data.tar.gz: 8f8259c854e992e72c18e48141509475378e3a91
3
+ metadata.gz: 2d3196fa0c3271fa922d6435a995279a40099389
4
+ data.tar.gz: c78180f6adf88588d048082a6431f9061f20f0be
5
5
  SHA512:
6
- metadata.gz: 8585f39f42b34a667c34f760c96b6c1dbeba03ab4d1adefc1bb26aa8a846038e26fba3e48f657007fb1973f10b4173e86e84e16b8b13f7bd20809adbd40e7f2e
7
- data.tar.gz: 620cf747186ef3518b9bec8f248cf8d78d4c73510ff745117fbbecdad66f24816124a26287485b77396043574e48aa5878bcc61c123f8a2c9b0d00502b83761f
6
+ metadata.gz: 415f9b8ce3a4579747b22a9b5fddfd3948f1af9b3136529ddfd33c39afcd87099f065c7e40563235881307453b7802035da57ba07c0e87b8c4423289f57839ed
7
+ data.tar.gz: 15cd19df7a2de8f764685a21e61f77160f52a9573b294639168b9d7844d712d7eab0ab432d579057045143acfb41512c97d2eea3e880d944eac159ede82fec70
data/History.md CHANGED
@@ -1,4 +1,12 @@
1
1
 
2
+ v1.8.0 / 2017-08-24
3
+ ===================
4
+
5
+ * Update manpages
6
+ * Update rubies and jruby on travis
7
+ * Remove encoding and other Style/SymbolArray fixes
8
+ * Add tidy validator for html5
9
+
2
10
  v1.7.0 / 2017-04-08
3
11
  ===================
4
12
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rake/testtask'
2
2
  require 'rubocop/rake_task'
3
3
 
4
- task default: [:test, :rubocop]
4
+ task default: %i(test rubocop)
5
5
 
6
6
  # install asciidoc libxml2-utils xmlto docbook-xsl docbook-xml
7
7
  desc 'Update manpage from asciidoc file'
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'paint'
3
2
 
4
3
  module ValidateWebsite
@@ -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 = [:crawl, :static].freeze
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
- @html5_validator_service_url = 'https://checker.html5.org/'
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
- class << self
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
- def initialize(original_doc, body, ignore = nil)
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(xml_doc, document_body)
95
+ def validate(xhtml_doc)
97
96
  if self.class.xsd(@namespace)
98
- self.class.xsd(@namespace).validate(xml_doc)
99
- elsif document_body =~ /^\<!DOCTYPE html\>/i
100
- html5_validate(document_body)
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
- doc = Dir.chdir(XHTML_PATH) do
110
+ xhtml_doc = Dir.chdir(XHTML_PATH) do
112
111
  Nokogiri::XML(document) { |cfg| cfg.noent.dtdload.dtdvalid }
113
112
  end
114
- @errors = validate(doc, document)
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(document)
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 results.errors
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,5 +2,5 @@
2
2
 
3
3
  # Version file for ValidateWebsite
4
4
  module ValidateWebsite
5
- VERSION = '1.7.0'.freeze
5
+ VERSION = '1.8.0'.freeze
6
6
  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: 01/04/2017
5
+ .\" Date: 06/20/2017
6
6
  .\" Manual: \ \&
7
7
  .\" Source: \ \&
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "VALIDATE\-WEBSITE\-S" "1" "01/04/2017" "\ \&" "\ \&"
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
@@ -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: 01/04/2017
5
+ .\" Date: 06/20/2017
6
6
  .\" Manual: \ \&
7
7
  .\" Source: \ \&
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "VALIDATE\-WEBSITE" "1" "01/04/2017" "\ \&" "\ \&"
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, "&amp;")
243
+ .replace(/</g, "&lt;")
244
+ .replace(/>/g, "&gt;")
245
+ .replace(/"/g, "&quot;")
246
+ .replace(/'/g, "&#039;")
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://linuxfr.org/","messages":[{"type":"info","lastLine":198,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the h1element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":195,"lastColumn":20,"firstColumn":1,"subType":"warning","message":"Section lacks heading. Consider using “h2”-“h6” elements to add identifying headings to all sections.","extract":"eil\"</h1>\n<section id=\"phare\">\n<arti","hiliteStart":10,"hiliteLength":20},{"type":"info","lastLine":313,"lastColumn":38,"firstColumn":7,"subType":"warning","message":"The “main” role is unnecessary for element “main”.","extract":"av>\n</nav><main id=\"contents\" role=\"main\">\n<arti","hiliteStart":10,"hiliteLength":32},{"type":"info","lastLine":316,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":374,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":429,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":488,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":546,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":624,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":663,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":739,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":781,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":845,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":908,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":967,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":1015,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"info","lastLine":1079,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40},{"type":"error","lastLine":1098,"lastColumn":18,"firstColumn":1,"message":"Duplicate ID “sommaire”.","extract":"ibre.</p>\n<h2 id=\"sommaire\">Sommai","hiliteStart":10,"hiliteLength":18},{"type":"info","lastLine":507,"lastColumn":18,"firstColumn":1,"subType":"warning","message":"The first occurrence of ID “sommaire” was here.","extract":"ibre.</p>\n<h2 id=\"sommaire\">Sommai","hiliteStart":10,"hiliteLength":18},{"type":"info","lastLine":1138,"lastColumn":40,"firstColumn":1,"subType":"warning","message":"Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).","extract":"\n<header>\n<h1 class=\"entry-title\" itemprop=\"name\"><a hre","hiliteStart":10,"hiliteLength":40}],"language":"fr"}
1
+ {"url":"https://www.rust-lang.org/en-US/","messages":[{"type":"error","lastLine":113,"lastColumn":6,"firstColumn":1,"message":"End tagpreseen, 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}]}
@@ -39,7 +39,7 @@ describe ValidateWebsite::Static do
39
39
  markup: false,
40
40
  not_found: true)
41
41
  end
42
- @validate_website.not_founds_count.must_equal 193
42
+ @validate_website.not_founds_count.must_equal 213
43
43
  end
44
44
  end
45
45
 
@@ -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
- it 'should have an array of errors' do
79
- validator = subject.new(@html5_page.doc,
80
- @html5_page.body)
81
- validator.valid?.must_equal false
82
- validator.errors.size.must_equal 1
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
- it 'should exclude errors ignored by :ignore option' do
86
- ignore = /Duplicate ID/
87
- validator = subject.new(@html5_page.doc,
88
- @html5_page.body,
89
- ignore)
90
- validator.valid?.must_equal true
91
- validator.errors.size.must_equal 0
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
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'webmock/minitest'
3
2
 
4
3
  # FakePage html helper for webmock
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.7.0
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-04-08 00:00:00.000000000 Z
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