hairaito 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4e3cd6878a89daabfb2fdf0d3a9ca661fc238566
4
+ data.tar.gz: 237ee8e16ee95f4e20ac67bd301500a3eb7deb81
5
+ SHA512:
6
+ metadata.gz: 762a12095a72d152e6b07d80c48ec8294f4077c4e769222d0e770bb3eaa2b7a4c0c4f5176502ddd57a5c6727c6aaaa073d70384623ed808afdcccd3ccc500e6a
7
+ data.tar.gz: 69e4d996242c807cd4fbd42e445809b6af8d64fbc3bf6d84bb519b787a7bd1a27887137177bd4f2e4295b3e06ace6967cfc7dd9c2edf7456093506f7231c241f
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hairaito.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Denis Mazilov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Hairaito
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'hairaito'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install hairaito
16
+
17
+ ## Usage
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it ( https://github.com/dmazilov/hairaito/fork )
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+
data/hairaito.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hairaito/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'hairaito'
8
+ spec.version = Hairaito::VERSION
9
+ spec.authors = ['Denis Mazilov']
10
+ spec.email = ['denis.mazilov@gmail.com']
11
+ spec.summary = %q{Extends Nokogiri with text snippets highlighting.}
12
+ spec.description = %q{}
13
+ spec.homepage = 'https://github.com/dmazilov/hairaito'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake'
23
+
24
+ spec.add_dependency 'nokogiri'
25
+ end
@@ -0,0 +1,86 @@
1
+ module Hairaito
2
+ module Nokogiri
3
+ module XML
4
+ module Document
5
+
6
+ def highlight(snippets, options = {})
7
+ highlighting_default_options(options)
8
+ snippets.each do |snippet|
9
+ highlighting_base.traverse_by_text(snippet).each do |snippet_container|
10
+ to_wrap = []
11
+ start_index = snippet_container.text().index(snippet)
12
+
13
+ start_node, start_inner_index = snippet_container.text_node_by_position(start_index)
14
+ start_range = start_node.text_range_by_index(start_inner_index, snippet.length)
15
+ to_wrap << [start_node, start_range]
16
+
17
+ # If start node contains only part of snippet
18
+ if snippet.length > start_range.size
19
+ end_node, end_inner_index = snippet_container.text_node_by_position(start_index + snippet.length - 1)
20
+ end_range = end_node.text_range_by_index(end_inner_index)
21
+ to_wrap += snippet_container.text_nodes_between(start_node, end_node).map do |node|
22
+ [node, 0..(node.text.length - 1)]
23
+ end
24
+ to_wrap << [end_node, end_range]
25
+ end
26
+
27
+ to_wrap.each do |node_data|
28
+ node_data.first.highlight_by_range(node_data.last)
29
+ end
30
+
31
+ snippet_container['class'] = "#{snippet_container['class']} #{@hl_opts[:snippet_container_class]}"
32
+ end
33
+ end
34
+ numerate_highlighted_snippets if @hl_opts[:numerate]
35
+ to_html
36
+ end
37
+
38
+ def highlight_snippet_part(text)
39
+ if @hl_opts[:snippet_part_wrapper].blank?
40
+ raise ArgumentError.new('Snippet part wrapper tag is not specified!')
41
+ end
42
+ wrapper = create_element("#{@hl_opts[:snippet_part_wrapper]}", class: "#{@hl_opts[:snippet_part_wrapper_class]}")
43
+ wrapper.content = text
44
+ wrapper
45
+ end
46
+
47
+ private
48
+
49
+ def highlighting_default_options(options)
50
+ @hl_opts = {
51
+ base_selector: 'body',
52
+ base_content_wrapper: '',
53
+ base_content_wrapper_class: 'highlighting-base',
54
+ snippet_container_class: 'highlighted-snippet',
55
+ snippet_part_wrapper: 'span',
56
+ snippet_part_wrapper_class: 'highlighted-snippet-part',
57
+ numerate: true,
58
+ numeration_attr: 'data-snippet-id',
59
+ numeration_prefix: '',
60
+ numeration_suffix: '',
61
+ }.merge(options)
62
+ end
63
+
64
+ def highlighting_base
65
+ base = at(@hl_opts[:base_selector])
66
+ raise ArgumentError.new('Document does not contain highlighting base element!') if base.blank?
67
+ if @hl_opts[:base_content_wrapper].present?
68
+ wrapper = create_element("#{@hl_opts[:base_content_wrapper]}", class: "#{@hl_opts[:base_content_wrapper_class]}")
69
+ base.children.each{|child| child.parent = wrapper}
70
+ wrapper.parent = base
71
+ return wrapper
72
+ end
73
+ base
74
+ end
75
+
76
+ def numerate_highlighted_snippets
77
+ css(".#{@hl_opts[:snippet_container_class]}").each_with_index do |snippet_container, index|
78
+ snippet_container[@hl_opts[:numeration_attr]] =
79
+ "#{@hl_opts[:numeration_prefix]}#{index}#{@hl_opts[:numeration_prefix]}"
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,66 @@
1
+ module Hairaito
2
+ module Nokogiri
3
+ module XML
4
+ module Node
5
+
6
+ def text_nodes
7
+ result_nodes = []
8
+ traverse do |node|
9
+ result_nodes << node if node.text?
10
+ end
11
+ result_nodes
12
+ ::Nokogiri::XML::NodeSet.new(document, result_nodes)
13
+ end
14
+
15
+ def text_nodes_between(start_node, end_node)
16
+ nodes = text_nodes
17
+ indexes = [nodes.index(start_node), nodes.index(end_node)]
18
+ raise ArgumentError.new('Node must contain both start and end nodes!') if indexes.compact.count < 2
19
+ # Start and end nodes are equals or are neighbours
20
+ return [] if indexes.last - indexes.first < 2
21
+ result_nodes = nodes.slice((indexes.first + 1)..(indexes.last - 1))
22
+ ::Nokogiri::XML::NodeSet.new(document, result_nodes)
23
+ end
24
+
25
+ def traverse_by_text(text, exclude_ancestors = true)
26
+ excluded = []
27
+ result_nodes = []
28
+ traverse do |node|
29
+ next if node.is_a?(::Nokogiri::XML::Text)
30
+ next if node.in?(excluded)
31
+ if node.text.include?(text)
32
+ result_nodes << node
33
+ excluded += node.ancestors if exclude_ancestors
34
+ end
35
+ end
36
+ result_nodes
37
+ end
38
+
39
+ def text_node_by_position(in_text_position)
40
+ text_nodes.each do |node|
41
+ # Node does not contain parent_index
42
+ if node.text.length - 1 < in_text_position
43
+ in_text_position -= node.text.length
44
+ next
45
+ end
46
+ return node, in_text_position
47
+ end
48
+ raise ArgumentError.new('Inner index is out of range!')
49
+ end
50
+
51
+ def highlight_by_range(range)
52
+ prefix = range.first > 0 ? text[0..(range.first - 1)]: ''
53
+ suffix = range.last < text.length - 1 ? text[(range.last + 1)..(text.length - 1)]: ''
54
+ for_wrapping = text[range]
55
+ new_contents = "#{prefix}#{document.highlight_snippet_part(for_wrapping)}#{suffix}"
56
+ replace(new_contents)
57
+ end
58
+
59
+ def text_range_by_index(index, demand_length = nil)
60
+ demand_length.present? ? index..[text.length - 1, index + demand_length - 1].min : 0..index
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,6 @@
1
+ module Hairaito
2
+ module Nokogiri
3
+ module XML
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module Hairaito
2
+ module Nokogiri
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Hairaito
2
+ VERSION = '0.0.2'
3
+ end
data/lib/hairaito.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'nokogiri'
2
+ require 'hairaito/version'
3
+
4
+ module Hairaito
5
+ end
6
+
7
+ directory = Pathname.new(File.dirname(__FILE__))
8
+ Dir.glob(directory.join('hairaito', '*.rb')) { |file| require file }
9
+ Dir.glob(directory.join('hairaito', '**/*.rb')) { |file| require file }
10
+
11
+ Nokogiri::XML::Document.send(:include, Hairaito::Nokogiri::XML::Document)
12
+ Nokogiri::XML::Node.send(:include, Hairaito::Nokogiri::XML::Node)
13
+
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hairaito
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Denis Mazilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: ''
56
+ email:
57
+ - denis.mazilov@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - hairaito.gemspec
68
+ - lib/hairaito.rb
69
+ - lib/hairaito/nokogiri.rb
70
+ - lib/hairaito/nokogiri/xml.rb
71
+ - lib/hairaito/nokogiri/xml/document.rb
72
+ - lib/hairaito/nokogiri/xml/node.rb
73
+ - lib/hairaito/version.rb
74
+ homepage: https://github.com/dmazilov/hairaito
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.2.2
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Extends Nokogiri with text snippets highlighting.
98
+ test_files: []