mirror of https://github.com/freeCodeCamp/devdocs
parent
4a654feb1b
commit
6d23cd241a
@ -0,0 +1,42 @@
|
|||||||
|
var HL = {
|
||||||
|
'javascript': [
|
||||||
|
'._coffeescript pre:last-child',
|
||||||
|
'._angular .prettyprint',
|
||||||
|
'._d3 .highlight > pre',
|
||||||
|
'._underscore pre',
|
||||||
|
'._node pre > code',
|
||||||
|
'._jquery .syntaxhighlighter .javascript',
|
||||||
|
'._ember pre .javascript',
|
||||||
|
['._knockout pre', 'data-bind="', false],
|
||||||
|
'._mdn pre[class*="js"]'
|
||||||
|
],
|
||||||
|
|
||||||
|
'c': [ '._ruby pre.c' ],
|
||||||
|
'ruby': [ '._ruby pre.ruby' ],
|
||||||
|
'coffeescript': [ '._coffeescript .code > pre:first-child' ],
|
||||||
|
'python': [ '._sphinx pre.python' ],
|
||||||
|
|
||||||
|
'markup': [
|
||||||
|
['._knockout pre', 'data-bind="', true],
|
||||||
|
'._ember pre.html',
|
||||||
|
'._mdn pre[class*="html"]'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function highlightAll(sels, language){
|
||||||
|
for(var i = 0; i < sels.length; ++i){
|
||||||
|
var sel = sels[i] instanceof Array ? sels[i] : [sels[i]];
|
||||||
|
var nodes = document.querySelectorAll(sel[0]);
|
||||||
|
|
||||||
|
for(var j = 0, c = nodes.length; j < c; ++j){
|
||||||
|
if(!sel[1] || nodes[j].innerHTML.indexOf(sel[i][1]) != -1 == sel[2]){
|
||||||
|
nodes[j].classList.add('language-' + language)
|
||||||
|
Prism.highlightElement(nodes[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var lang in HL)
|
||||||
|
if(HL.hasOwnProperty(lang))
|
||||||
|
highlightAll(HL[lang], lang);
|
@ -0,0 +1,22 @@
|
|||||||
|
._devhelp {
|
||||||
|
width: 90%;
|
||||||
|
height: auto !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
|
||||||
|
pre, code, samp, ._redis > .example {
|
||||||
|
font-family: "Lucida Console", "Sans Mono", "Courier New", monospace;
|
||||||
|
font-size: 1.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
._content {
|
||||||
|
height: auto !important;
|
||||||
|
margin-left: 0px !important;
|
||||||
|
overflow-y: visible !important;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,218 @@
|
|||||||
|
require 'find'
|
||||||
|
require 'fileutils'
|
||||||
|
require 'nokogiri'
|
||||||
|
require 'json'
|
||||||
|
require 'downloader'
|
||||||
|
|
||||||
|
class DevHelp
|
||||||
|
SKIP_ASSETS = %{.json .js .gz}
|
||||||
|
DEVHELP_NS = 'http://www.devhelp.net/book'
|
||||||
|
|
||||||
|
def initialize(options)
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def book_options(doc)
|
||||||
|
{
|
||||||
|
xmlns: DEVHELP_NS,
|
||||||
|
title: doc.name,
|
||||||
|
name: "#{doc.slug}-#{doc.version}",
|
||||||
|
version: 2,
|
||||||
|
author: '',
|
||||||
|
language: doc.language,
|
||||||
|
link: 'index.html'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize_url(link)
|
||||||
|
link.gsub(/^([^.]+?)(?=$|#)/, '\1.html\2')
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_devhelp(doc, structure)
|
||||||
|
builder do |xml|
|
||||||
|
xml.book book_options(doc) do
|
||||||
|
xml.doc.create_internal_subset('book', '-//W3C//DTD HTML 4.01 Transitional//EN', '')
|
||||||
|
xml.chapters do
|
||||||
|
structure[:terms].each do |term, link|
|
||||||
|
xml.sub name: term, link: normalize_url(link)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.to_xml
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_docs(*docs)
|
||||||
|
docs.flatten.each(&method(:for_doc))
|
||||||
|
end
|
||||||
|
|
||||||
|
def cp_r(src, dst)
|
||||||
|
t = File.dirname(dst)
|
||||||
|
FileUtils.mkdir_p(t) unless File.directory?(t)
|
||||||
|
FileUtils.cp_r(src, dst)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_assets(src, dst)
|
||||||
|
cp_r(src, dst)
|
||||||
|
Find.find(dst).select do |file|
|
||||||
|
ext = File.extname(file).downcase
|
||||||
|
File.unlink(file) if File.file?(file) && SKIP_ASSETS.include?(ext)
|
||||||
|
ext == '.css'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_js(js, dst)
|
||||||
|
js.map do |file|
|
||||||
|
npath = File.join(dst, File.basename(file))
|
||||||
|
FileUtils.cp(file, npath)
|
||||||
|
npath
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_devhelp_file(doc, src, dst, unlink = false)
|
||||||
|
structure = parse_index(src)
|
||||||
|
File.write(dst, build_devhelp(doc, structure))
|
||||||
|
File.unlink(src)
|
||||||
|
structure
|
||||||
|
end
|
||||||
|
|
||||||
|
def downloader
|
||||||
|
@downloader ||= Downloader.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def document_structure(doc, type)
|
||||||
|
doc.internal_subset.remove
|
||||||
|
doc.create_internal_subset('html', nil, nil)
|
||||||
|
|
||||||
|
unless doc.at_css('head')
|
||||||
|
title = Nokogiri::XML::Node.new 'head', doc
|
||||||
|
doc.root.children.first.add_previous_sibling title
|
||||||
|
end
|
||||||
|
|
||||||
|
body = doc.at_css('body')
|
||||||
|
content = body.children.remove
|
||||||
|
|
||||||
|
body.add_child <<-EOF
|
||||||
|
<section class="_container _devhelp">
|
||||||
|
<div class="_content _#{type}">
|
||||||
|
#{content}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_title(doc, text = nil)
|
||||||
|
unless text
|
||||||
|
h1 = doc.at_css('h1, h2, h3, h4, h5')
|
||||||
|
return unless h1
|
||||||
|
text = h1.text.strip
|
||||||
|
end
|
||||||
|
|
||||||
|
title = doc.at_css('title')
|
||||||
|
unless title
|
||||||
|
title = Nokogiri::XML::Node.new 'title', doc
|
||||||
|
doc.at_css('head').add_child(title)
|
||||||
|
end
|
||||||
|
|
||||||
|
title.content = text
|
||||||
|
end
|
||||||
|
|
||||||
|
def inject_assets(doc, path, css, js, skip)
|
||||||
|
head = doc.at_css('head')
|
||||||
|
level = ['..'] * path[skip].count(File::SEPARATOR)
|
||||||
|
|
||||||
|
css.each do |asset|
|
||||||
|
link = Nokogiri::XML::Node.new 'link', doc
|
||||||
|
link['rel'] = 'stylesheet'
|
||||||
|
link['media'] = 'all'
|
||||||
|
link['charset'] = 'UTF-8'
|
||||||
|
link['href'] = File.join(level + [asset[skip]])
|
||||||
|
head.add_child(link)
|
||||||
|
end
|
||||||
|
|
||||||
|
body = doc.at_css('body')
|
||||||
|
js.each do |asset|
|
||||||
|
script = Nokogiri::XML::Node.new 'script', doc
|
||||||
|
script['type'] = 'text/javascript'
|
||||||
|
script['charset'] = 'UTF-8'
|
||||||
|
script['src'] = File.join(level + [asset[skip]])
|
||||||
|
body.add_child(script)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def src_for(doc)
|
||||||
|
src_path = File.join(@options[:base_path], doc.path)
|
||||||
|
|
||||||
|
unless File.exists?(src_path)
|
||||||
|
puts %(ERROR: can't find "#{doc.name}" documentation files. Please download/scrape it first.)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
src_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def dst_for(doc)
|
||||||
|
dst_path = File.join(@options[:devhelp_path], doc.path)
|
||||||
|
|
||||||
|
if File.exists?(dst_path)
|
||||||
|
unless @options[:force]
|
||||||
|
puts %(ERROR: #{doc.name} was already converted. Use --force to overwrite.)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
FileUtils.rm_rf(dst_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
dst_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_doc(doc)
|
||||||
|
src_path = src_for(doc) || return
|
||||||
|
dst_path = dst_for(doc) || return
|
||||||
|
|
||||||
|
cp_r(src_path, dst_path)
|
||||||
|
|
||||||
|
css = prepare_assets(@options[:asset_path], File.join(dst_path, 'assets'))
|
||||||
|
js = prepare_js(@options[:js], File.join(dst_path, 'assets'))
|
||||||
|
|
||||||
|
titles = make_devhelp_file(doc,
|
||||||
|
File.join(dst_path, @options[:index]),
|
||||||
|
File.join(dst_path, "#{doc.slug}.devhelp2"),
|
||||||
|
true)
|
||||||
|
|
||||||
|
skip = (dst_path.length + 1) .. -1
|
||||||
|
|
||||||
|
downloader.processor do |file, parser|
|
||||||
|
document_structure(parser, doc.type)
|
||||||
|
set_title(parser, titles[:files][file[skip]])
|
||||||
|
inject_assets(parser, file, css, js, skip)
|
||||||
|
end
|
||||||
|
|
||||||
|
Find.find(dst_path).
|
||||||
|
select(&method(:is_document?)).
|
||||||
|
each {|d| downloader.process_page(nil, d)}
|
||||||
|
|
||||||
|
downloader.run
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_document?(p)
|
||||||
|
!File.basename(p).starts_with?('.') && p.ends_with?('.html') && File.file?(p)
|
||||||
|
end
|
||||||
|
|
||||||
|
def builder(&block)
|
||||||
|
Nokogiri::XML::Builder.new(encoding: 'UTF-8', &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_index(path)
|
||||||
|
structure = {terms: {}, files: {}}
|
||||||
|
|
||||||
|
JSON.load(open(path))['entries'].each do |e, o|
|
||||||
|
structure[:terms][e['name']] = e['path']
|
||||||
|
|
||||||
|
unless e['path'].include?('#')
|
||||||
|
structure[:files][e['path']] = e['name']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
structure
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,161 @@
|
|||||||
|
require 'typhoeus'
|
||||||
|
require 'nokogiri'
|
||||||
|
require 'delegate'
|
||||||
|
require 'fileutils'
|
||||||
|
require 'cgi'
|
||||||
|
|
||||||
|
class Downloader < SimpleDelegator
|
||||||
|
include Typhoeus
|
||||||
|
|
||||||
|
MAX_QUEUE_SIZE = 20
|
||||||
|
|
||||||
|
def initialize(*args)
|
||||||
|
super(Hydra.new(*args))
|
||||||
|
end
|
||||||
|
|
||||||
|
def processor(&block)
|
||||||
|
@processor = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def queue_size
|
||||||
|
queued_requests.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def file(src, dst, &block)
|
||||||
|
file = nil
|
||||||
|
|
||||||
|
request = Request.new(src)
|
||||||
|
|
||||||
|
request.on_headers do |response|
|
||||||
|
if response.response_code == 200
|
||||||
|
dname = File.dirname(dst)
|
||||||
|
FileUtils.mkdir_p(dname) unless File.directory?(dname)
|
||||||
|
file = open(dst, 'wb')
|
||||||
|
else
|
||||||
|
failed(src, dst, response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
request.on_body do |chunk|
|
||||||
|
file.write(chunk) if file
|
||||||
|
end
|
||||||
|
|
||||||
|
request.on_complete do |response|
|
||||||
|
if file
|
||||||
|
file.close
|
||||||
|
block.call(dst) if block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
queue request
|
||||||
|
dst
|
||||||
|
end
|
||||||
|
|
||||||
|
def queue(*args, &block)
|
||||||
|
run while queue_size > MAX_QUEUE_SIZE
|
||||||
|
__getobj__(*args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def page(src, target)
|
||||||
|
file(src, target) { process_page(src, target) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_page(src, path)
|
||||||
|
doc = Nokogiri::HTML.parse(File.read(path), 'UTF-8')
|
||||||
|
rdir = path.gsub(%r{\.[^./]*$}, '') + '_files'
|
||||||
|
skip = dirname_range(path)
|
||||||
|
|
||||||
|
doc.css('iframe[src], img[src], script[src], link[href][rel="stylesheet"], link[href][rel="shortcut icon"]').each do |elem|
|
||||||
|
uri = url_join(src, elem['src'] || elem['href'])
|
||||||
|
|
||||||
|
case elem.name
|
||||||
|
when 'iframe'
|
||||||
|
elem['src'] = page(uri, resource_path_for(rdir, uri, 'html'))[skip]
|
||||||
|
when 'link'
|
||||||
|
elem['href'] = file(uri, resource_path_for(rdir, uri, 'css')) do |f|
|
||||||
|
process_stylesheet_file(uri, f) if elem['rel'] == 'stylesheet'
|
||||||
|
end[skip]
|
||||||
|
when 'script'
|
||||||
|
elem['src'] = file(uri, resource_path_for(rdir, uri, 'js'))[skip]
|
||||||
|
when 'img'
|
||||||
|
elem['src'] = file(uri, resource_path_for(rdir, uri, 'png'))[skip]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
doc.css('style').each do |style|
|
||||||
|
style.content = process_stylesheet(src, style.content, rdir)
|
||||||
|
end
|
||||||
|
|
||||||
|
@processor.call(path, doc) if @processor
|
||||||
|
|
||||||
|
File.write(path, doc.to_html)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def dirname_range(path, dirname = false)
|
||||||
|
path = File.dirname(path) unless dirname
|
||||||
|
l = path.length
|
||||||
|
l += 1 if l > 0
|
||||||
|
l..-1
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_stylesheet_file(src, fname)
|
||||||
|
File.write(fname, process_stylesheet(src, File.read(fname), File.dirname(fname)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_stylesheet(src, style, dir)
|
||||||
|
skip = dirname_range(dir, true)
|
||||||
|
|
||||||
|
style = style.gsub(/@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\)]*)\)?[;\n]?/) do
|
||||||
|
uri = url_join(src, $1)
|
||||||
|
fname = resource_path_for(dir, uri, 'css')
|
||||||
|
file(uri, fname) { process_stylesheet_file(uri, fname) }
|
||||||
|
%{@import url("#{fname[skip]}") #$2;\n}
|
||||||
|
end
|
||||||
|
|
||||||
|
style = style.gsub(/(?!@import )url\s*\(["']?(.+?)["']?\)/) do
|
||||||
|
uri = url_join(src, $1)
|
||||||
|
fname = resource_path_for(dir, uri, 'png')
|
||||||
|
file(uri, fname)
|
||||||
|
%{url("#{fname[skip]}")}
|
||||||
|
end
|
||||||
|
|
||||||
|
style
|
||||||
|
end
|
||||||
|
|
||||||
|
def url_join(base, new)
|
||||||
|
if base && new
|
||||||
|
URI.join(base, new).to_s
|
||||||
|
else
|
||||||
|
base || new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_path_for(dir, resource, ext = nil)
|
||||||
|
rfile = CGI.unescape(resource.gsub(%r{.*/|[#?].*}, ''))
|
||||||
|
|
||||||
|
if rfile.empty?
|
||||||
|
rfile = "downloaded#{'%04d' % @counter}"
|
||||||
|
@counter += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
rfile << ".#{ext}" if ext && File.extname(rfile).empty?
|
||||||
|
|
||||||
|
prefix = 1
|
||||||
|
tfile = rfile
|
||||||
|
|
||||||
|
puts rfile
|
||||||
|
|
||||||
|
loop do
|
||||||
|
path = File.join(dir, tfile)
|
||||||
|
break path unless File.exists?(path)
|
||||||
|
tfile = "#{prefix}_#{rfile}"
|
||||||
|
prefix += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def failed(src, dst, response)
|
||||||
|
puts "#{src} -> #{dst} failed: #{response.status_message}"
|
||||||
|
end
|
||||||
|
end
|
@ -1 +1 @@
|
|||||||
[]
|
[{"name":"Angular.js","slug":"angular","type":"angular","version":"1.0.7","index_path":"angular/index.json","mtime":1376320383},{"name":"Backbone.js","slug":"backbone","type":"underscore","version":"1.1.0","index_path":"backbone/index.json","mtime":1381653808},{"name":"CoffeeScript","slug":"coffeescript","type":"coffeescript","version":"1.6.3","index_path":"coffeescript/index.json","mtime":1381655883},{"name":"CSS","slug":"css","type":"mdn","version":null,"index_path":"css/index.json","mtime":1386415462},{"name":"D3.js","slug":"d3","type":"d3","version":"3.4.1","index_path":"d3/index.json","mtime":1390150423},{"name":"DOM","slug":"dom","type":"mdn","version":null,"index_path":"dom/index.json","mtime":1386432655},{"name":"DOM Events","slug":"dom_events","type":"mdn","version":null,"index_path":"dom_events/index.json","mtime":1381908561},{"name":"Ember.js","slug":"ember","type":"ember","version":"1.3.0","index_path":"ember/index.json","mtime":1389536714},{"name":"Git","slug":"git","type":"git","version":"1.8.5","index_path":"git/index.json","mtime":1386958029},{"name":"HTML","slug":"html","type":"mdn","version":null,"index_path":"html/index.json","mtime":1386413422},{"name":"HTTP","slug":"http","type":"rfc","version":null,"index_path":"http/index.json","mtime":1381675313},{"name":"JavaScript","slug":"javascript","type":"mdn","version":null,"index_path":"javascript/index.json","mtime":1386425468},{"name":"jQuery","slug":"jquery","type":"jquery","version":"up to 2.0.3","index_path":"jquery/index.json","mtime":1388954827},{"name":"jQuery Mobile","slug":"jquerymobile","type":"jquery","version":"1.4.0","index_path":"jquerymobile/index.json","mtime":1388956495},{"name":"jQuery UI","slug":"jqueryui","type":"jquery","version":"1.10.3","index_path":"jqueryui/index.json","mtime":1388955780},{"name":"Knockout.js","slug":"knockout","type":"knockout","version":"3.0.0","index_path":"knockout/index.json","mtime":1390167124},{"name":"Less","slug":"less","type":"less","version":"1.6.0","index_path":"less/index.json","mtime":1388929594},{"name":"Lo-Dash","slug":"lodash","type":"lodash","version":"2.4.1","index_path":"lodash/index.json","mtime":1386144288},{"name":"Node.js","slug":"node","type":"node","version":"0.10.24","index_path":"node/index.json","mtime":1387611542},{"name":"PHP","slug":"php","type":"php","version":"up to 5.5.8","index_path":"php/index.json","mtime":1389535846},{"name":"PostgreSQL","slug":"postgresql","type":"postgres","version":"up to 9.3.2","index_path":"postgresql/index.json","mtime":1387061550},{"name":"Python","slug":"python","type":"sphinx","version":"3.3.3","index_path":"python/index.json","mtime":1385469242},{"name":"Ruby on Rails","slug":"rails","type":"rdoc","version":"4.0.2","index_path":"rails/index.json","mtime":1386228555},{"name":"Redis","slug":"redis","type":"redis","version":"up to 2.8.4","index_path":"redis/index.json","mtime":1389660376},{"name":"Ruby","slug":"ruby","type":"rdoc","version":"2.1.0","index_path":"ruby/index.json","mtime":1388961427},{"name":"Sass","slug":"sass","type":"yard","version":"3.2.12","index_path":"sass/index.json","mtime":1381679946},{"name":"Underscore.js","slug":"underscore","type":"underscore","version":"1.5.2","index_path":"underscore/index.json","mtime":1381654139}]
|
Loading…
Reference in new issue