Kaynağa Gözat

Add Angular 2 documentation

Thibaut Courouble 9 yıl önce
ebeveyn
işleme
b78244fb53

BIN
assets/images/icons.png


BIN
assets/images/icons@2x.png


+ 1 - 0
assets/javascripts/app/app.coffee

@@ -116,6 +116,7 @@
       needsSaving = true
       doc = @disabledDocs.findBy('slug', 'node~4_lts') if slug == 'node~4.2_lts'
       doc = @disabledDocs.findBy('slug', 'xslt_xpath') if slug == 'xpath'
+      doc = @disabledDocs.findBy('slug', "angularjs~#{match[1]}") if match = /^angular~(1\.\d)$/.exec(slug)
       doc ||= @disabledDocs.findBy('slug_without_version', slug)
       if doc
         @disabledDocs.remove(doc)

+ 4 - 4
assets/javascripts/news.json

@@ -1,5 +1,8 @@
 [
   [
+    "2016-06-12",
+    "New documentation: <a href=\"/angular/\">Angular 2</a>"
+  ], [
     "2016-06-05",
     "New documentation: <a href=\"/kotlin/\">Kotlin</a> and <a href=\"/padrino/\">Padrino</a>"
   ], [
@@ -154,9 +157,6 @@
   ], [
     "2014-02-12",
     "The root/category pages are now included in the search index (e.g. <a href=\"/#q=CSS\">CSS</a>)"
-  ], [
-    "2014-01-26",
-    "Updated <a href=\"/angular/\">Angular.js</a> documentation"
   ], [
     "2014-01-19",
     "New <a href=\"/d3/\">D3.js</a> and <a href=\"/knockout/\">Knockout.js</a> documentations"
@@ -210,7 +210,7 @@
     "URL search now automatically opens the first result."
   ], [
     "2013-08-13",
-    "New <a href=\"/angular/\">Angular.js</a> documentation"
+    "New <a href=\"/angularjs/\">Angular.js</a> documentation"
   ], [
     "2013-08-11",
     "New <a href=\"/sass/\">Sass</a> and <a href=\"/less/\">Less</a> documentations"

+ 1 - 1
assets/javascripts/templates/pages/about_tmpl.coffee

@@ -86,7 +86,7 @@ app.templates.aboutPage = -> """
 """
 
 credits = [
-  [ 'Angular.js',
+  [ 'Angular<br>Angular.js',
     '2010-2016 Google, Inc.',
     'CC BY',
     'https://creativecommons.org/licenses/by/4.0/'

+ 1 - 0
assets/javascripts/views/pages/simple.coffee

@@ -7,6 +7,7 @@ class app.views.SimplePage extends app.views.BasePage
     return
 
 app.views.AngularPage =
+app.views.AngularjsPage =
 app.views.CakephpPage =
 app.views.ElixirPage =
 app.views.EmberPage =

+ 1 - 0
assets/stylesheets/application-dark.css.scss

@@ -30,6 +30,7 @@
 
 @import 'pages/simple',
         'pages/angular',
+        'pages/angularjs',
         'pages/apache',
         'pages/bower',
         'pages/c',

+ 1 - 0
assets/stylesheets/application.css.scss

@@ -30,6 +30,7 @@
 
 @import 'pages/simple',
         'pages/angular',
+        'pages/angularjs',
         'pages/apache',
         'pages/bower',
         'pages/c',

+ 2 - 1
assets/stylesheets/global/_icons.scss

@@ -38,7 +38,7 @@
 ._icon-node_lts:before      { background-position: -6rem -1rem; }
 ._icon-sass:before          { background-position: -7rem -1rem; }
 ._icon-less:before          { background-position: -8rem -1rem; }
-._icon-angular:before       { background-position: -9rem -1rem; }
+._icon-angularjs:before     { background-position: -9rem -1rem; }
 ._icon-coffeescript:before  { background-position: 0 -2rem; @extend %darkIconFix !optional; }
 ._icon-ember:before         { background-position: -1rem -2rem; }
 %icon-menu                  { background-position: -2rem -2rem; }
@@ -141,3 +141,4 @@
 ._icon-numpy:before         { background-position: -5rem -11rem; }
 ._icon-kotlin:before        { background-position: -6rem -11rem; }
 ._icon-padrino:before       { background-position: -7rem -11rem; }
+._icon-angular:before       { background-position: -8rem -11rem; }

+ 36 - 37
assets/stylesheets/pages/_angular.scss

@@ -1,54 +1,53 @@
 ._angular {
-  h2 { @extend %block-heading; }
+  padding-left: 1rem;
 
-  //
-  // Index
-  //
+  h1, h2, > h3, .banner, .badges { margin-left: -1rem; }
 
-  .nav-index-section {
-    margin: 1.5em 0 1em -2em;
-    list-style: none;
-    font-weight: bold;
-    text-transform: capitalize;
-  }
+  ._mobile & {
+    padding-left: 0;
 
-  //
-  // Other
-  //
+    h1, h2, > h3, .banner, .badges { margin-left: 0; }
+  }
 
-  h3, h4 { font-size: 1rem; }
+  h2 { @extend %block-heading; }
+  > h3 { @extend %block-label, %label-blue; }
 
-  .alert { @extend %note; }
-  .alert-success { @extend %note-green; }
-  .alert-error { @extend %note-red; }
+  p > code, .status-badge { @extend %label; }
 
-  p > code, li > code, td > code { @extend %label; }
+  .l-sub-section, .alert, .banner { @extend %note; }
+  .banner { @extend %note-green; }
+  .alert.is-important { @extend %note-red; }
+  .alert.is-helpful { @extend %note-blue; }
 
-  .view-source, .improve-docs {
-    position: relative;
-    float: right;
-    line-height: 1.7rem;
-    padding-left: 1em;
-    font-size: .875rem;
-    background: $contentBackground;
+  td > h3, .l-sub-section > h3, .l-sub-section > h4, .alert > h3, .alert > h4 {
+    margin-top: .25rem;
+    font-size: 1em;
   }
 
-  .defs {
-    padding-left: 1rem;
-    list-style: none;
+  img {
+    display: block;
+    margin: 1em auto;
 
-    > li > h3:first-child {
-      margin: 0 0 1em -1rem;
-      @extend %block-label, %label-blue;
+    &[align="left"] {
+      float: left;
+      margin: 0 1em 0 0;
     }
 
-    > li + li { margin-top: 2em; }
-
-    h4 {
-      margin: 1em 0 .5em;
-      font-size: 1em;
+    &[align="right"] {
+      float: right;
+      margin: 0 0 0 1em;
     }
+  }
+
+  .location-badge {
+    text-align: right;
+    font-style: italic;
+  }
+
+  .filetree {
+    white-space: normal;
+    @extend %pre;
 
-    ul { list-style-type: disc; }
+    .children { padding-left: 1em; }
   }
 }

+ 54 - 0
assets/stylesheets/pages/_angularjs.scss

@@ -0,0 +1,54 @@
+._angularjs {
+  h2 { @extend %block-heading; }
+
+  //
+  // Index
+  //
+
+  .nav-index-section {
+    margin: 1.5em 0 1em -2em;
+    list-style: none;
+    font-weight: bold;
+    text-transform: capitalize;
+  }
+
+  //
+  // Other
+  //
+
+  h3, h4 { font-size: 1rem; }
+
+  .alert { @extend %note; }
+  .alert-success { @extend %note-green; }
+  .alert-error { @extend %note-red; }
+
+  p > code, li > code, td > code { @extend %label; }
+
+  .view-source, .improve-docs {
+    position: relative;
+    float: right;
+    line-height: 1.7rem;
+    padding-left: 1em;
+    font-size: .875rem;
+    background: $contentBackground;
+  }
+
+  .defs {
+    padding-left: 1rem;
+    list-style: none;
+
+    > li > h3:first-child {
+      margin: 0 0 1em -1rem;
+      @extend %block-label, %label-blue;
+    }
+
+    > li + li { margin-top: 2em; }
+
+    h4 {
+      margin: 1em 0 .5em;
+      font-size: 1em;
+    }
+
+    ul { list-style-type: disc; }
+  }
+}

+ 6 - 2
lib/app.rb

@@ -280,12 +280,16 @@ class App < Sinatra::Application
     'iojs' => 'node',
     'yii1' => 'yii~1.1',
     'python2' => 'python~2.7',
-    'xpath' => 'xslt_xpath'
+    'xpath' => 'xslt_xpath',
+    'angular~1.5' => 'angularjs~1.5',
+    'angular~1.4' => 'angularjs~1.4',
+    'angular~1.3' => 'angularjs~1.3',
+    'angular~1.2' => 'angularjs~1.2'
   }
 
   get %r{\A/([\w~\.]+)(\-[\w\-]+)?(/.*)?\z} do |doc, type, rest|
     return redirect "/#{DOC_REDIRECTS[doc]}#{type}#{rest}" if DOC_REDIRECTS.key?(doc)
-    return redirect "/angular/api#{rest}", 301 if doc == 'angular' && rest.start_with?('/ng')
+    return redirect "/angularjs/api#{rest}", 301 if doc == 'angular' && rest.start_with?('/ng')
     return 404 unless @doc = find_doc(doc)
 
     if rest.nil?

+ 49 - 60
lib/docs/filters/angular/clean_html.rb

@@ -2,96 +2,85 @@ module Docs
   class Angular
     class CleanHtmlFilter < Filter
       def call
-        root_page? ? root : other
+        container = at_css('article.docs-content')
+        container.child.before(at_css('header.hero h1')).before(css('header.hero .badges')).before(css('header.hero + .banner'))
+        @doc = container
 
-        # Remove ng-* attributes
-        css('*').each do |node|
-          node.attributes.each_key do |attribute|
-            node.remove_attribute(attribute) if attribute.start_with? 'ng-'
-          end
+        css('pre.no-bg-with-indent').each do |node|
+          node.content = '  ' + node.content.gsub("\n", "\n  ")
         end
 
-        doc
-      end
-
-      def root
-        css('.nav-index-group').each do |node|
-          if heading = node.at_css('.nav-index-group-heading')
-            heading.name = 'h2'
-          end
-          node.parent.before(node.children)
+        css('.openParens').each do |node|
+          node.parent.name = 'pre'
+          node.parent.content = node.parent.css('code, pre').map(&:content).join("\n")
         end
 
-        css('.nav-index-section').each do |node|
-          node.content = node.content
+        css('button.verbose', 'button.verbose + .l-verbose-section', 'a[id=top]', 'a[href="#top"]').remove
+
+        css('.c10', '.showcase', '.showcase-content', '.l-main-section', 'div.div', 'div[flex]', 'code-tabs', 'md-card', 'md-card-content', 'div:not([class])', 'footer', '.card-row', '.card-row-container', 'figure', 'blockquote', 'exported', 'defined', 'div.ng-scope').each do |node|
+          node.before(node.children).remove
         end
 
-        css('.toc-close', '.naked-list').remove
-      end
+        css('span.badges').each do |node|
+          node.name = 'div'
+        end
 
-      def other
-        css('#example', '.example', '#description_source', '#description_demo', '[id$="example"]', 'hr').remove
+        css('pre[language]').each do |node|
+          node['data-language'] = node['language'].sub(/\Ats/, 'typescript').strip
+        end
 
-        css('header').each do |node|
-          node.before(node.children).remove
+        css('pre.prettyprint').each do |node|
+          node.content = node.content.strip
         end
 
-        if h1 = at_css('h1')
-          h1.prepend_child(css('.view-source', '.improve-docs'))
+        css('a[id]:empty').each do |node|
+          node.next_element['id'] = node['id'] if node.next_element
         end
 
-        # Remove root-level <div>
-        while div = at_css('h1 + div')
-          div.before(div.children)
-          div.remove
+        css('a[name]:empty').each do |node|
+          node.next_element['id'] = node['name'] if node.next_element
         end
 
-        css('.api-profile-header-structure > li').each do |node|
-          node.inner_html = node.inner_html.remove('- ')
+        css('tr[style]').each do |node|
+          node.remove_attribute 'style'
         end
 
-        css('h1').each_with_index do |node, i|
-          next if i == 0
+        css('h1:not(:first-child)').each do |node|
           node.name = 'h2'
-        end
+        end unless at_css('h2')
 
-        # Remove examples
-        css('.runnable-example').each do |node|
-          node.parent.remove
+        css('img[style]').each do |node|
+          node['align'] ||= node['style'][/float:\s*(left|right)/, 1]
+          node['style'] = node['style'].split(';').map(&:strip).select { |s| s =~ /\Awidth|height/ }.join(';')
         end
 
-        # Remove dead links (e.g. ngRepeat)
-        css('a.type-hint').each do |node|
-          node.name = 'code'
-          node.remove_attribute 'href'
+        css('.example-title + pre').each do |node|
+          node['name'] = node.previous_element.content.strip
+          node.previous_element.remove
         end
 
-        css('pre > code').each do |node|
-          node['class'] ||= ''
-          lang = if node['class'].include?('lang-html') || node.content =~ /\A</
-            'html'
-          elsif node['class'].include?('lang-css')
-            'css'
-          elsif node['class'].include?('lang-js') || node['class'].include?('lang-javascript')
-            'javascript'
+        css('pre[name]').each do |node|
+          case node['data-language']
+          when 'html' then node.content = "<!-- #{node['name']} -->\n\n" + node.content
+          when 'css'  then node.content = "/* #{node['name']} */\n\n" + node.content
+          else             node.content = "// #{node['name']}\n\n" + node.content
           end
-          node.parent['data-language'] = lang if lang
+        end
 
-          node.before(node.children).remove
+        css('a.is-button > h3').each do |node|
+          node.parent.content = node.content
         end
 
-        # Remove some <code> elements
-        css('h1 > code', 'h2 > code', 'h3 > code', 'h4 > code', 'h6 > code').each do |node|
-          node.before(node.content).remove
+        css('#angular-2-glossary ~ .l-sub-section').each do |node|
+          node.before(node.children).remove
         end
 
-        css('ul.methods', 'ul.properties', 'ul.events').add_class('defs').each do |node|
-          node.css('> li > h3').each do |h3|
-            next if h3.content.present?
-            h3.content = h3.next_element.content
-            h3.next_element.remove
-          end
+        location_badge = at_css('.location-badge')
+        if location_badge && doc.last_element_child != location_badge
+          doc.last_element_child.after(location_badge)
         end
+
+        doc
       end
     end
   end

+ 25 - 34
lib/docs/filters/angular/entries.rb

@@ -2,51 +2,42 @@ module Docs
   class Angular
     class EntriesFilter < Docs::EntriesFilter
       def get_name
-        if slug.start_with?('api')
-          name = URI.unescape(slug).split('/').last
-          name.remove! %r{\Ang\.}
-          name << " (#{subtype})" if subtype == 'directive' || subtype == 'filter'
-          name.prepend("#{type}.") unless type.starts_with?('ng ') || name == type
-          name
-        elsif slug.start_with?('guide')
-          name = URI.decode(at_css('.improve-docs')['href'][/message=docs\(guide%2F(.+?)\)/, 1])
-          name.prepend 'Guide: '
-          name
+        name = at_css('header.hero h1').content.strip
+        name = name.split(':').first
+
+        if mod
+          if name == 'Testing'
+            return "#{mod.capitalize} Testing"
+          elsif name == 'Index' || name == 'Angular'
+            return mod
+          end
         end
+
+        name << '()' if at_css('.status-badge').try(:content) == 'Function'
+        name
       end
 
       def get_type
-        if slug.start_with?('api')
-          type = slug.split('/').drop(1).first
-          type << " #{subtype}s" if type == 'ng' && subtype
-          type
-        elsif slug.start_with?('guide')
+        if slug.start_with?('guide/')
           'Guide'
+        else
+          type = at_css('.is-nav-title-selected').content.strip
+          type.remove! ' Reference'
+          type << ": #{mod}" if mod
+          type
         end
       end
 
-      def subtype
-        return @subtype if defined? @subtype
-        node = at_css '.api-profile-header-structure'
-        data = node.content.match %r{(\w+?) in module} if node
-        @subtype = data && data[1]
-      end
+      INDEX = Set.new
 
-      def additional_entries
-        return [] unless slug.start_with?('api')
-        entries = []
+      def include_default_entry?
+        INDEX.add?([name, type].join(';')) ? true : false # ¯\_(ツ)_/¯
+      end
 
-        css('ul.defs').each do |list|
-          list.css('> li[id]').each do |node|
-            next unless heading = node.at_css('h3')
-            name = heading.content.strip
-            name.sub! %r{\(.*\);}, '()'
-            name.prepend "#{self.name.split.first}."
-            entries << [name, node['id']]
-          end
-        end
+      private
 
-        entries
+      def mod
+        @mod ||= slug[/api\/([\w\-]+)\//, 1]
       end
     end
   end

+ 98 - 0
lib/docs/filters/angularjs/clean_html.rb

@@ -0,0 +1,98 @@
+module Docs
+  class Angularjs
+    class CleanHtmlFilter < Filter
+      def call
+        root_page? ? root : other
+
+        # Remove ng-* attributes
+        css('*').each do |node|
+          node.attributes.each_key do |attribute|
+            node.remove_attribute(attribute) if attribute.start_with? 'ng-'
+          end
+        end
+
+        doc
+      end
+
+      def root
+        css('.nav-index-group').each do |node|
+          if heading = node.at_css('.nav-index-group-heading')
+            heading.name = 'h2'
+          end
+          node.parent.before(node.children)
+        end
+
+        css('.nav-index-section').each do |node|
+          node.content = node.content
+        end
+
+        css('.toc-close', '.naked-list').remove
+      end
+
+      def other
+        css('#example', '.example', '#description_source', '#description_demo', '[id$="example"]', 'hr').remove
+
+        css('header').each do |node|
+          node.before(node.children).remove
+        end
+
+        if h1 = at_css('h1')
+          h1.prepend_child(css('.view-source', '.improve-docs'))
+        end
+
+        # Remove root-level <div>
+        while div = at_css('h1 + div')
+          div.before(div.children)
+          div.remove
+        end
+
+        css('.api-profile-header-structure > li').each do |node|
+          node.inner_html = node.inner_html.remove('- ')
+        end
+
+        css('h1').each_with_index do |node, i|
+          next if i == 0
+          node.name = 'h2'
+        end
+
+        # Remove examples
+        css('.runnable-example').each do |node|
+          node.parent.remove
+        end
+
+        # Remove dead links (e.g. ngRepeat)
+        css('a.type-hint').each do |node|
+          node.name = 'code'
+          node.remove_attribute 'href'
+        end
+
+        css('pre > code').each do |node|
+          node['class'] ||= ''
+          lang = if node['class'].include?('lang-html') || node.content =~ /\A</
+            'html'
+          elsif node['class'].include?('lang-css')
+            'css'
+          elsif node['class'].include?('lang-js') || node['class'].include?('lang-javascript')
+            'javascript'
+          end
+          node.parent['data-language'] = lang if lang
+
+          node.before(node.children).remove
+        end
+
+        # Remove some <code> elements
+        css('h1 > code', 'h2 > code', 'h3 > code', 'h4 > code', 'h6 > code').each do |node|
+          node.before(node.content).remove
+        end
+
+        css('ul.methods', 'ul.properties', 'ul.events').add_class('defs').each do |node|
+          node.css('> li > h3').each do |h3|
+            next if h3.content.present?
+            h3.content = h3.next_element.content
+            h3.next_element.remove
+          end
+        end
+      end
+    end
+  end
+end

+ 1 - 1
lib/docs/filters/angular/clean_urls.rb → lib/docs/filters/angularjs/clean_urls.rb

@@ -1,5 +1,5 @@
 module Docs
-  class Angular
+  class Angularjs
     class CleanUrlsFilter < Filter
       def call
         html.gsub! %r{angularjs\.org/([\d\.]+)/docs/partials/(\w+)/}, 'angularjs.org/\1/docs/\2/'

+ 53 - 0
lib/docs/filters/angularjs/entries.rb

@@ -0,0 +1,53 @@
+module Docs
+  class Angularjs
+    class EntriesFilter < Docs::EntriesFilter
+      def get_name
+        if slug.start_with?('api')
+          name = URI.unescape(slug).split('/').last
+          name.remove! %r{\Ang\.}
+          name << " (#{subtype})" if subtype == 'directive' || subtype == 'filter'
+          name.prepend("#{type}.") unless type.starts_with?('ng ') || name == type
+          name
+        elsif slug.start_with?('guide')
+          name = URI.decode(at_css('.improve-docs')['href'][/message=docs\(guide%2F(.+?)\)/, 1])
+          name.prepend 'Guide: '
+          name
+        end
+      end
+
+      def get_type
+        if slug.start_with?('api')
+          type = slug.split('/').drop(1).first
+          type << " #{subtype}s" if type == 'ng' && subtype
+          type
+        elsif slug.start_with?('guide')
+          'Guide'
+        end
+      end
+
+      def subtype
+        return @subtype if defined? @subtype
+        node = at_css '.api-profile-header-structure'
+        data = node.content.match %r{(\w+?) in module} if node
+        @subtype = data && data[1]
+      end
+
+      def additional_entries
+        return [] unless slug.start_with?('api')
+        entries = []
+
+        css('ul.defs').each do |list|
+          list.css('> li[id]').each do |node|
+            next unless heading = node.at_css('h3')
+            name = heading.content.strip
+            name.sub! %r{\(.*\);}, '()'
+            name.prepend "#{self.name.split.first}."
+            entries << [name, node['id']]
+          end
+        end
+
+        entries
+      end
+    end
+  end
+end

+ 39 - 48
lib/docs/scrapers/angular.rb

@@ -1,68 +1,59 @@
 module Docs
   class Angular < UrlScraper
-    self.name = 'Angular.js'
-    self.slug = 'angular'
     self.type = 'angular'
-    self.root_path = 'api.html'
-    self.initial_paths = %w(guide.html)
-
-    html_filters.push 'angular/clean_html', 'angular/entries', 'title'
-    text_filters.push 'angular/clean_urls'
-
-    options[:title] = false
-    options[:root_title] = 'Angular.js'
-
-    options[:decode_and_clean_paths] = true
-    options[:fix_urls_before_parse] = ->(str) do
-      str.gsub!('[', '%5B')
-      str.gsub!(']', '%5D')
-      str
-    end
-
-    options[:fix_urls] = ->(url) do
-      %w(api guide).each do |str|
-        url.sub! "/partials/#{str}/#{str}/", "/partials/#{str}/"
-        url.sub! %r{/#{str}/img/}, "/img/"
-        url.sub! %r{/#{str}/(.+?)/#{str}/}, "/#{str}/"
-        url.sub! %r{/partials/#{str}/(.+?)(?<!\.html)(?:\z|(#.*))}, "/partials/#{str}/\\1.html\\2"
-        url.sub! %r{/partials/.+/#{str}/}, "/partials/#{str}/"
-      end
+    self.root_path = 'api/'
+    self.links = {
+      home: 'https://angular.io/',
+      code: 'https://github.com/angular/angular'
+    }
+
+    html_filters.push 'angular/entries', 'angular/clean_html'
+
+    options[:skip_patterns] = [/deprecated/]
+    options[:skip] = %w(
+      index.html
+      styleguide.html
+      quickstart.html
+      guide/cheatsheet.html
+      guide/style-guide.html)
+
+    options[:replace_paths] = {
+      'testing/index.html' => 'guide/testing.html',
+      'glossary.html'      => 'guide/glossary.html',
+      'tutorial'           => 'tutorial/'
+    }
+
+    options[:fix_urls] = -> (url) do
+      url.sub! %r{\A(https://angular\.io/docs/.+/)index\.html\z}, '\1'
+      url.sub! %r{\A(https://angular\.io/docs/.+/index)/\z}, '\1'
       url
     end
 
-    options[:only_patterns] = [%r{\Aapi/}, %r{\Aguide/}]
-    options[:skip] = %w(api/ng.html)
-
     options[:attribution] = <<-HTML
       &copy; 2010&ndash;2016 Google, Inc.<br>
       Licensed under the Creative Commons Attribution License 4.0.
     HTML
 
-    stub '' do
+    stub 'api/' do
       capybara = load_capybara_selenium
-      capybara.app_host = 'https://code.angularjs.org'
-      capybara.visit("/#{self.class.release}/docs/api")
-      capybara.execute_script("return document.querySelector('.side-navigation').innerHTML")
-    end
-
-    version '1.5' do
-      self.release = '1.5.6'
-      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+      capybara.app_host = 'https://angular.io'
+      capybara.visit('/docs/ts/latest/api/')
+      capybara.execute_script('return document.body.innerHTML')
     end
 
-    version '1.4' do
-      self.release = '1.4.11'
-      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    version '2.0 TypeScript' do
+      self.release = '2.0.0rc1'
+      self.base_url = "https://angular.io/docs/ts/latest/"
     end
 
-    version '1.3' do
-      self.release = '1.3.20'
-      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
-    end
+    private
 
-    version '1.2' do
-      self.release = '1.2.29'
-      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    def parse(string)
+      string.gsub! '<code-example', '<pre'
+      string.gsub! '</code-example', '</pre'
+      string.gsub! '<code-pane', '<pre'
+      string.gsub! '</code-pane', '</pre'
+      super string
     end
   end
 end

+ 68 - 0
lib/docs/scrapers/angularjs.rb

@@ -0,0 +1,68 @@
+module Docs
+  class Angularjs < UrlScraper
+    self.name = 'Angular.js'
+    self.slug = 'angularjs'
+    self.type = 'angularjs'
+    self.root_path = 'api.html'
+    self.initial_paths = %w(guide.html)
+
+    html_filters.push 'angularjs/clean_html', 'angularjs/entries', 'title'
+    text_filters.push 'angularjs/clean_urls'
+
+    options[:title] = false
+    options[:root_title] = 'Angular.js'
+
+    options[:decode_and_clean_paths] = true
+    options[:fix_urls_before_parse] = ->(str) do
+      str.gsub!('[', '%5B')
+      str.gsub!(']', '%5D')
+      str
+    end
+
+    options[:fix_urls] = ->(url) do
+      %w(api guide).each do |str|
+        url.sub! "/partials/#{str}/#{str}/", "/partials/#{str}/"
+        url.sub! %r{/#{str}/img/}, "/img/"
+        url.sub! %r{/#{str}/(.+?)/#{str}/}, "/#{str}/"
+        url.sub! %r{/partials/#{str}/(.+?)(?<!\.html)(?:\z|(#.*))}, "/partials/#{str}/\\1.html\\2"
+        url.sub! %r{/partials/.+/#{str}/}, "/partials/#{str}/"
+      end
+      url
+    end
+
+    options[:only_patterns] = [%r{\Aapi/}, %r{\Aguide/}]
+    options[:skip] = %w(api/ng.html)
+
+    options[:attribution] = <<-HTML
+      &copy; 2010&ndash;2016 Google, Inc.<br>
+      Licensed under the Creative Commons Attribution License 4.0.
+    HTML
+
+    stub '' do
+      capybara = load_capybara_selenium
+      capybara.app_host = 'https://code.angularjs.org'
+      capybara.visit("/#{self.class.release}/docs/api")
+      capybara.execute_script("return document.querySelector('.side-navigation').innerHTML")
+    end
+
+    version '1.5' do
+      self.release = '1.5.6'
+      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    end
+
+    version '1.4' do
+      self.release = '1.4.11'
+      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    end
+
+    version '1.3' do
+      self.release = '1.3.20'
+      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    end
+
+    version '1.2' do
+      self.release = '1.2.29'
+      self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
+    end
+  end
+end

BIN
public/icons/docs/angular/16.png


BIN
public/icons/docs/angular/16@2x.png


+ 1 - 1
public/icons/docs/angular/SOURCE

@@ -1 +1 @@
-https://github.com/angular/angular.js/tree/master/images/logo/AngularJS-Shield.exports
+https://angular.io/presskit.html

BIN
public/icons/docs/angularjs/16.png


BIN
public/icons/docs/angularjs/16@2x.png


+ 1 - 0
public/icons/docs/angularjs/SOURCE

@@ -0,0 +1 @@
+https://github.com/angular/angular.js/tree/master/images/logo/AngularJS-Shield.exports