From 83c9071a91deff6bd249f0494ba7dc4931a636d7 Mon Sep 17 00:00:00 2001 From: Braulio Bhavamitra Date: Mon, 28 Sep 2015 18:30:26 -0300 Subject: [PATCH] Add comment_paragraph --- plugins/comment_paragraph/README.md | 4 ++++ plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb | 12 ++++++++++++ plugins/comment_paragraph/controllers/myprofile/comment_paragraph_plugin_myprofile_controller.rb | 18 ++++++++++++++++++ plugins/comment_paragraph/controllers/profile/comment_paragraph_plugin_profile_controller.rb | 14 ++++++++++++++ plugins/comment_paragraph/controllers/public/comment_paragraph_plugin_public_controller.rb | 9 +++++++++ plugins/comment_paragraph/db/migrate/20141223184902_add_paragraph_uuid_to_comments.rb | 6 ++++++ plugins/comment_paragraph/lib/comment_paragraph_plugin.rb | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/lib/comment_paragraph_plugin/macros/allow_comment.rb | 23 +++++++++++++++++++++++ plugins/comment_paragraph/lib/ext/article.rb | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/lib/ext/comment.rb | 22 ++++++++++++++++++++++ plugins/comment_paragraph/lib/ext/tinymce_helper.rb | 11 +++++++++++ plugins/comment_paragraph/po/comment_paragraph.pot | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/po/pt/comment_paragraph.po | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/public/comment_paragraph_macro.js | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/public/images/internet-group-chat.png | Bin 0 -> 364 bytes plugins/comment_paragraph/public/rangy-core.js | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/public/rangy-cssclassapplier.js | 32 ++++++++++++++++++++++++++++++++ plugins/comment_paragraph/public/rangy-serializer.js | 23 +++++++++++++++++++++++ plugins/comment_paragraph/public/style.css | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/functional/comment_paragraph_plugin_admin_controller_test.rb | 38 ++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/functional/comment_paragraph_plugin_myprofile_controller_test.rb | 35 +++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/functional/comment_paragraph_plugin_profile_controller_test.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/functional/comment_paragraph_plugin_public_controller_test.rb | 29 +++++++++++++++++++++++++++++ plugins/comment_paragraph/test/functional/content_viewer_controller_test.rb | 29 +++++++++++++++++++++++++++++ plugins/comment_paragraph/test/test_helper.rb | 5 +++++ plugins/comment_paragraph/test/unit/allow_comment_test.rb | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/unit/article_test.rb | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/unit/comment_paragraph_plugin_test.rb | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/comment_paragraph/test/unit/comment_test.rb | 26 ++++++++++++++++++++++++++ plugins/comment_paragraph/test/unit/tinymce_helper_test.rb | 27 +++++++++++++++++++++++++++ plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb | 25 +++++++++++++++++++++++++ plugins/comment_paragraph/views/comment_paragraph_plugin_profile/_comment_paragraph.html.erb | 24 ++++++++++++++++++++++++ plugins/comment_paragraph/views/comment_paragraph_plugin_profile/comment_extra.html.erb | 3 +++ plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb | 17 +++++++++++++++++ 34 files changed, 1746 insertions(+), 0 deletions(-) create mode 100644 plugins/comment_paragraph/README.md create mode 100644 plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb create mode 100644 plugins/comment_paragraph/controllers/myprofile/comment_paragraph_plugin_myprofile_controller.rb create mode 100644 plugins/comment_paragraph/controllers/profile/comment_paragraph_plugin_profile_controller.rb create mode 100644 plugins/comment_paragraph/controllers/public/comment_paragraph_plugin_public_controller.rb create mode 100644 plugins/comment_paragraph/db/migrate/20141223184902_add_paragraph_uuid_to_comments.rb create mode 100644 plugins/comment_paragraph/lib/comment_paragraph_plugin.rb create mode 100644 plugins/comment_paragraph/lib/comment_paragraph_plugin/macros/allow_comment.rb create mode 100644 plugins/comment_paragraph/lib/ext/article.rb create mode 100644 plugins/comment_paragraph/lib/ext/comment.rb create mode 100644 plugins/comment_paragraph/lib/ext/tinymce_helper.rb create mode 100644 plugins/comment_paragraph/po/comment_paragraph.pot create mode 100644 plugins/comment_paragraph/po/pt/comment_paragraph.po create mode 100644 plugins/comment_paragraph/public/comment_paragraph_macro.js create mode 100644 plugins/comment_paragraph/public/images/internet-group-chat.png create mode 100644 plugins/comment_paragraph/public/rangy-core.js create mode 100644 plugins/comment_paragraph/public/rangy-cssclassapplier.js create mode 100644 plugins/comment_paragraph/public/rangy-serializer.js create mode 100644 plugins/comment_paragraph/public/style.css create mode 100644 plugins/comment_paragraph/test/functional/comment_paragraph_plugin_admin_controller_test.rb create mode 100644 plugins/comment_paragraph/test/functional/comment_paragraph_plugin_myprofile_controller_test.rb create mode 100644 plugins/comment_paragraph/test/functional/comment_paragraph_plugin_profile_controller_test.rb create mode 100644 plugins/comment_paragraph/test/functional/comment_paragraph_plugin_public_controller_test.rb create mode 100644 plugins/comment_paragraph/test/functional/content_viewer_controller_test.rb create mode 100644 plugins/comment_paragraph/test/test_helper.rb create mode 100644 plugins/comment_paragraph/test/unit/allow_comment_test.rb create mode 100644 plugins/comment_paragraph/test/unit/article_test.rb create mode 100644 plugins/comment_paragraph/test/unit/comment_paragraph_plugin_test.rb create mode 100644 plugins/comment_paragraph/test/unit/comment_test.rb create mode 100644 plugins/comment_paragraph/test/unit/tinymce_helper_test.rb create mode 100644 plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb create mode 100644 plugins/comment_paragraph/views/comment_paragraph_plugin_profile/_comment_paragraph.html.erb create mode 100644 plugins/comment_paragraph/views/comment_paragraph_plugin_profile/comment_extra.html.erb create mode 100644 plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb diff --git a/plugins/comment_paragraph/README.md b/plugins/comment_paragraph/README.md new file mode 100644 index 0000000..a75fd45 --- /dev/null +++ b/plugins/comment_paragraph/README.md @@ -0,0 +1,4 @@ +README - Comment Paragraph Plugin +====================================== + +This plugins enables all the paragraphs of an article to have comments diff --git a/plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb b/plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb new file mode 100644 index 0000000..fd4a85b --- /dev/null +++ b/plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb @@ -0,0 +1,12 @@ +class CommentParagraphPluginAdminController < AdminController + append_view_path File.join(File.dirname(__FILE__) + '/../views') + + def index + @settings = Noosfero::Plugin::Settings.new(environment, CommentParagraphPlugin, params[:settings]) + if request.post? + @settings.save! + session[:notice] = _('Settings successfuly saved') + end + end + +end diff --git a/plugins/comment_paragraph/controllers/myprofile/comment_paragraph_plugin_myprofile_controller.rb b/plugins/comment_paragraph/controllers/myprofile/comment_paragraph_plugin_myprofile_controller.rb new file mode 100644 index 0000000..56b0acc --- /dev/null +++ b/plugins/comment_paragraph/controllers/myprofile/comment_paragraph_plugin_myprofile_controller.rb @@ -0,0 +1,18 @@ +class CommentParagraphPluginMyprofileController < MyProfileController + + before_filter :check_permission + + def toggle_activation + @article.comment_paragraph_plugin_activate = !@article.comment_paragraph_plugin_activate + @article.save! + redirect_to @article.view_url + end + + protected + + def check_permission + @article = profile.articles.find(params[:id]) + render_access_denied unless @article.comment_paragraph_plugin_enabled? && @article.allow_edit?(user) + end + +end diff --git a/plugins/comment_paragraph/controllers/profile/comment_paragraph_plugin_profile_controller.rb b/plugins/comment_paragraph/controllers/profile/comment_paragraph_plugin_profile_controller.rb new file mode 100644 index 0000000..9743c3e --- /dev/null +++ b/plugins/comment_paragraph/controllers/profile/comment_paragraph_plugin_profile_controller.rb @@ -0,0 +1,14 @@ +class CommentParagraphPluginProfileController < ProfileController + append_view_path File.join(File.dirname(__FILE__) + '/../../views') + + def view_comments + @article_id = params[:article_id] + @paragraph_uuid = params[:paragraph_uuid] + article = profile.articles.find(@article_id) + @comments = article.comments.without_spam.in_paragraph(@paragraph_uuid) + @comments_count = @comments.count + @comments = @comments.without_reply + render :partial => 'comment/comment.html.erb', :collection => @comments + end + +end diff --git a/plugins/comment_paragraph/controllers/public/comment_paragraph_plugin_public_controller.rb b/plugins/comment_paragraph/controllers/public/comment_paragraph_plugin_public_controller.rb new file mode 100644 index 0000000..e34d4b9 --- /dev/null +++ b/plugins/comment_paragraph/controllers/public/comment_paragraph_plugin_public_controller.rb @@ -0,0 +1,9 @@ +class CommentParagraphPluginPublicController < PublicController + append_view_path File.join(File.dirname(__FILE__) + '/../views') + + def comment_paragraph + @comment = Comment.find(params[:id]) + render :json => { :paragraph_uuid => @comment.paragraph_uuid } + end + +end diff --git a/plugins/comment_paragraph/db/migrate/20141223184902_add_paragraph_uuid_to_comments.rb b/plugins/comment_paragraph/db/migrate/20141223184902_add_paragraph_uuid_to_comments.rb new file mode 100644 index 0000000..93c09e8 --- /dev/null +++ b/plugins/comment_paragraph/db/migrate/20141223184902_add_paragraph_uuid_to_comments.rb @@ -0,0 +1,6 @@ +class AddParagraphUuidToComments < ActiveRecord::Migration + def change + add_column :comments, :paragraph_uuid, :string + add_index :comments, :paragraph_uuid + end +end diff --git a/plugins/comment_paragraph/lib/comment_paragraph_plugin.rb b/plugins/comment_paragraph/lib/comment_paragraph_plugin.rb new file mode 100644 index 0000000..f0809fe --- /dev/null +++ b/plugins/comment_paragraph/lib/comment_paragraph_plugin.rb @@ -0,0 +1,60 @@ +class CommentParagraphPlugin < Noosfero::Plugin + + def self.plugin_name + "Comment Paragraph" + end + + def self.plugin_description + _("A plugin that display comments divided by paragraphs.") + end + + def unavailable_comments(scope) + scope.without_paragraph + end + + def comment_form_extra_contents(args) + comment = args[:comment] + paragraph_uuid = comment.paragraph_uuid || args[:paragraph_uuid] + proc { + arr = [] + arr << hidden_field_tag('comment[id]', comment.id) + arr << hidden_field_tag('comment[paragraph_uuid]', paragraph_uuid) if paragraph_uuid + arr << hidden_field_tag('comment[comment_paragraph_selected_area]', comment.comment_paragraph_selected_area) unless comment.comment_paragraph_selected_area.blank? + arr << hidden_field_tag('comment[comment_paragraph_selected_content]', comment.comment_paragraph_selected_content) unless comment.comment_paragraph_selected_content.blank? + arr + } + end + + def comment_extra_contents(args) + comment = args[:comment] + proc { + render :file => 'comment_paragraph_plugin_profile/comment_extra', :locals => {:comment => comment} + } + end + + def js_files + ['comment_paragraph_macro', 'rangy-core', 'rangy-cssclassapplier', 'rangy-serializer'] + end + + def stylesheet? + true + end + + def self.activation_mode_default_setting + 'manual' + end + + def article_extra_toolbar_buttons(article) + user = context.send :user + return [] if !article.comment_paragraph_plugin_enabled? || !article.allow_edit?(user) + { + :title => article.comment_paragraph_plugin_activated? ? _('Deactivate Comments') : _('Activate Comments'), + :url => {:controller => 'comment_paragraph_plugin_myprofile', :profile => article.profile.identifier, :action => 'toggle_activation', :id => article.id}, + :icon => :toggle_comment_paragraph + } + + end + +end + +require_dependency 'comment_paragraph_plugin/macros/allow_comment' diff --git a/plugins/comment_paragraph/lib/comment_paragraph_plugin/macros/allow_comment.rb b/plugins/comment_paragraph/lib/comment_paragraph_plugin/macros/allow_comment.rb new file mode 100644 index 0000000..4f88899 --- /dev/null +++ b/plugins/comment_paragraph/lib/comment_paragraph_plugin/macros/allow_comment.rb @@ -0,0 +1,23 @@ +ActionView::Base.sanitized_allowed_attributes += ['data-macro', 'data-macro-paragraph_uuid'] + +class CommentParagraphPlugin::AllowComment < Noosfero::Plugin::Macro + + def self.configuration + { :params => [] } + end + + def parse(params, inner_html, source) + paragraph_uuid = params[:paragraph_uuid] + article = source + count = article.paragraph_comments.without_spam.in_paragraph(paragraph_uuid).count + + proc { + if controller.kind_of?(ContentViewerController) && article.comment_paragraph_plugin_activated? + render :partial => 'comment_paragraph_plugin_profile/comment_paragraph', + :locals => {:paragraph_uuid => paragraph_uuid, :article_id => article.id, :inner_html => inner_html, :count => count, :profile_identifier => article.profile.identifier } + else + inner_html + end + } + end +end diff --git a/plugins/comment_paragraph/lib/ext/article.rb b/plugins/comment_paragraph/lib/ext/article.rb new file mode 100644 index 0000000..01ccd84 --- /dev/null +++ b/plugins/comment_paragraph/lib/ext/article.rb @@ -0,0 +1,62 @@ +require_dependency 'article' + +class Article + + has_many :paragraph_comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc', :conditions => [ 'paragraph_uuid IS NOT NULL'] + + before_save :comment_paragraph_plugin_parse_html + + settings_items :comment_paragraph_plugin_activate, :type => :boolean, :default => false + + def comment_paragraph_plugin_enabled? + environment.plugin_enabled?(CommentParagraphPlugin) && self.kind_of?(TextArticle) + end + + def comment_paragraph_plugin_activated? + comment_paragraph_plugin_activate && comment_paragraph_plugin_enabled? + end + + def cache_key_with_comment_paragraph(params = {}, user = nil, language = 'en') + cache_key_without_comment_paragraph(params, user, language) + (user.present? ? '-logged_in-': '-not_logged-') + end + + alias_method_chain :cache_key, :comment_paragraph + + def comment_paragraph_plugin_paragraph_content(paragraph_uuid) + doc = Nokogiri::HTML(body) + paragraph = doc.css("[data-macro-paragraph_uuid='#{paragraph_uuid}']").first + paragraph.present? ? paragraph.text : nil + end + + protected + + def comment_paragraph_plugin_parse_html + comment_paragraph_plugin_set_initial_value unless persisted? + return unless comment_paragraph_plugin_activated? + if body && (body_changed? || setting_changed?(:comment_paragraph_plugin_activate)) + updated = body_changed? ? body_change[1] : body + doc = Nokogiri::HTML(updated) + doc.css('li, body > div, body > span, body > p').each do |paragraph| + next if paragraph.css('[data-macro="comment_paragraph_plugin/allow_comment"]').present? || paragraph.content.blank? + + commentable = Nokogiri::XML::Node.new("span", doc) + commentable['class'] = "macro article_comments paragraph_comment #{paragraph['class']}" + commentable['data-macro'] = 'comment_paragraph_plugin/allow_comment' + commentable['data-macro-paragraph_uuid'] = SecureRandom.uuid + commentable.inner_html = paragraph.inner_html + paragraph.inner_html = commentable + end + self.body = doc.at('body').inner_html + end + end + + def comment_paragraph_plugin_set_initial_value + self.comment_paragraph_plugin_activate = comment_paragraph_plugin_enabled? && + comment_paragraph_plugin_settings.activation_mode == 'auto' + end + + def comment_paragraph_plugin_settings + @comment_paragraph_plugin_settings ||= Noosfero::Plugin::Settings.new(environment, CommentParagraphPlugin) + end + +end diff --git a/plugins/comment_paragraph/lib/ext/comment.rb b/plugins/comment_paragraph/lib/ext/comment.rb new file mode 100644 index 0000000..edc26de --- /dev/null +++ b/plugins/comment_paragraph/lib/ext/comment.rb @@ -0,0 +1,22 @@ +require_dependency 'comment' + +class Comment + + scope :without_paragraph, :conditions => {:paragraph_uuid => nil } + + settings_items :comment_paragraph_selected_area, :type => :string + settings_items :comment_paragraph_selected_content, :type => :string + + scope :in_paragraph, proc { |paragraph_uuid| { + :conditions => ['paragraph_uuid = ?', paragraph_uuid] + } + } + + attr_accessible :paragraph_uuid, :comment_paragraph_selected_area, :id, :comment_paragraph_selected_content + + before_validation do |comment| + comment.comment_paragraph_selected_area = nil if comment.comment_paragraph_selected_area.blank? + comment.comment_paragraph_selected_content = nil if comment_paragraph_selected_content.blank? + end + +end diff --git a/plugins/comment_paragraph/lib/ext/tinymce_helper.rb b/plugins/comment_paragraph/lib/ext/tinymce_helper.rb new file mode 100644 index 0000000..ac33968 --- /dev/null +++ b/plugins/comment_paragraph/lib/ext/tinymce_helper.rb @@ -0,0 +1,11 @@ +require_dependency 'tinymce_helper' + +module TinymceHelper + + def tinymce_init_js_with_comment_paragraph(options = {}) + options = options.merge(:keep_styles => false) if environment.plugin_enabled?(CommentParagraphPlugin) + tinymce_init_js_without_comment_paragraph(options) + end + + alias_method_chain :tinymce_init_js, :comment_paragraph +end diff --git a/plugins/comment_paragraph/po/comment_paragraph.pot b/plugins/comment_paragraph/po/comment_paragraph.pot new file mode 100644 index 0000000..dddf2ed --- /dev/null +++ b/plugins/comment_paragraph/po/comment_paragraph.pot @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 1.2~rc1-2843-g999a037\n" +"POT-Creation-Date: 2015-08-06 08:53-0300\n" +"PO-Revision-Date: 2015-03-03 07:41-0300\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb:8 +msgid "Settings successfuly saved" +msgstr "" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:8 +msgid "A plugin that display comments divided by paragraphs." +msgstr "" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:50 +msgid "Deactivate Comments" +msgstr "" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:50 +msgid "Activate Comments" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:2 +msgid "Comment Paragraph Plugin Settings" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:7 +msgid "Activation Mode" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:10 +msgid "Auto" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:11 +msgid "(all text articles will be activated by default)" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:15 +msgid "Manual" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:16 +msgid "(click on \"Activate Comment Paragraph\" )" +msgstr "" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:21 +msgid "Save" +msgstr "" + +#: plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb:2 +msgid "Title: " +msgstr "" + +#: plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb:11 +msgid "Paragraph: " +msgstr "" diff --git a/plugins/comment_paragraph/po/pt/comment_paragraph.po b/plugins/comment_paragraph/po/pt/comment_paragraph.po new file mode 100644 index 0000000..85b149a --- /dev/null +++ b/plugins/comment_paragraph/po/pt/comment_paragraph.po @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 1.2~rc1-2843-g999a037\n" +"POT-Creation-Date: 2015-08-06 08:53-0300\n" +"PO-Revision-Date: 2015-03-03 07:41-0300\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: plugins/comment_paragraph/controllers/comment_paragraph_plugin_admin_controller.rb:8 +msgid "Settings successfuly saved" +msgstr "Configurações salvas com sucesso" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:8 +msgid "A plugin that display comments divided by paragraphs." +msgstr "Um plugin que mostra os comentários por paragráfos" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:50 +msgid "Deactivate Comments" +msgstr "Desativar Comentários" + +#: plugins/comment_paragraph/lib/comment_paragraph_plugin.rb:50 +msgid "Activate Comments" +msgstr "Ativar Comentários" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:2 +msgid "Comment Paragraph Plugin Settings" +msgstr "Configurações do Plugin de Comentário por Parágrafos" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:7 +msgid "Activation Mode" +msgstr "Modo de Ativação" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:10 +msgid "Auto" +msgstr "Automático" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:11 +msgid "(all text articles will be activated by default)" +msgstr "(todos os artigos de texto serão ativados por padrão)" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:15 +msgid "Manual" +msgstr "Manual" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:16 +msgid "(click on \"Activate Comment Paragraph\" )" +msgstr "(clicar em \"Ativar Comentário por Parágrafo\")" + +#: plugins/comment_paragraph/views/comment_paragraph_plugin_admin/index.html.erb:21 +msgid "Save" +msgstr "Salvar" + +#: plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb:2 +msgid "Title: " +msgstr "Título: " + +#: plugins/comment_paragraph/views/tasks/_approve_comment_accept_details.html.erb:11 +msgid "Paragraph: " +msgstr "Parágrafo: " diff --git a/plugins/comment_paragraph/public/comment_paragraph_macro.js b/plugins/comment_paragraph/public/comment_paragraph_macro.js new file mode 100644 index 0000000..5a4c166 --- /dev/null +++ b/plugins/comment_paragraph/public/comment_paragraph_macro.js @@ -0,0 +1,237 @@ +jQuery(document).ready(function($) { + //Quit if does not detect a comment for that plugin + if($('.comment_paragraph').size() < 1) + return; + + $(document).keyup(function(e) { + // on press ESC key... + if (e.which == 27) { + hideCommentBox(); + } + }); + + $('.display-comment-form').unbind(); + $('.display-comment-form').click(function(){ + var $button = $(this); + showBox($button.parents('.post_comment_box')); + $($button).hide(); + $button.closest('.page-comment-form').find('input').first().focus(); + return false; + }); + + //Clears all old selected_area and selected_content after submit comment + $('[name|=commit]').click(function(){ + $('.selected_area').val(""); + $('.selected_content').val(""); + }); + + //hide comments when click outside + $('body').click(function(event){ + if ($(event.target).closest('.comment-paragraph-plugin, #comment-bubble').length === 0) { + hideCommentBox(); + $('#comment-bubble').removeClass('visible'); + hideAllSelectedAreasExcept(); + } + }); + + function hideCommentBox() { + $("div.side-comment").hide(); + $('.comment-paragraph-plugin').removeClass('comment-paragraph-slide-left'); + $('.comments').removeClass('selected'); + } + + function showBox(div){ + if(div.hasClass('closed')) { + div.removeClass('closed'); + div.addClass('opened'); + } + } + + rangy.init(); + cssApplier = rangy.createCssClassApplier("commented-area", {normalize: false}); + cssApplierSelected = rangy.createCssClassApplier("commented-area-selected", {normalize: false}); + + //Add marked text bubble + $("body").append('\ + \ +
Comentar
\ +
'); + + $('.comment-paragraph-plugin .side-comments-counter').click(function(){ + var container = $(this).closest('.comment-paragraph-plugin'); + var paragraphId = container.data('paragraph'); + hideAllSelectedAreasExcept(paragraphId); + hideCommentBox(); + $('#comment-bubble').removeClass('visible'); + container.addClass('comment-paragraph-slide-left selected'); + container.find('.side-comment').show(); + if(!$('body').hasClass('logged-in') && $('meta[name="profile.allow_unauthenticated_comments"]').length == 0) { + container.addClass('require-login-popup'); + } + //Loads the comments + var url = container.find('.side-comment').data('comment_paragraph_url'); + $.ajax(url).done(function(data) { + container.find('.article-comments-list').html(data); + if(container.find('.article-comment').length==0 || container.find('.selected_area').length) { + container.find('.post_comment_box a.display-comment-form').click(); + } else { + container.find('.post_comment_box').removeClass('opened'); + container.find('.post_comment_box').addClass('closed'); + container.find('.display-comment-form').show(); + } + }); + }); + + + $('#comment-bubble').click(function(event){ + var paragraph = $("#comment-bubble").data("paragraph"); + $('#comment-paragraph-plugin_' + paragraph).find('.side-comments-counter').click(); + }); + + function hideAllSelectedAreasExcept(clickedParagraph, areaClass) { + if(!areaClass) { + areaClass = '.commented-area'; + } + $(".comment_paragraph").each(function(){ + paragraph = $(this).closest('.comment-paragraph-plugin').data('paragraph'); + if(paragraph != clickedParagraph){ + $(this).find(areaClass).contents().unwrap(); + $(this).html($(this).html()); //XXX: workaround to prevent creation of text nodes + } + }); + } + + function getSelectionText() { + var text = ""; + if (window.getSelection) { + text = window.getSelection().toString(); + } else if (document.selection && document.selection.type != "Control") { + text = document.selection.createRange().text; + } + return text; + } + + function clearSelection() { + if ( document.selection ) { + document.selection.empty(); + } else if ( window.getSelection ) { + window.getSelection().removeAllRanges(); + } + } + + function setCommentBubblePosition(posX, posY) { + $("#comment-bubble").css({ + top: (posY - 80), + left: (posX - 70) + }); + } + + //highlight area from the paragraph + $('.comment_paragraph').mouseup(function(event) { + + hideCommentBox(); + + //Don't do anything if there is no selected text + if (getSelectionText().length == 0) { + return; + } + + var container = $(this).closest('.comment-paragraph-plugin'); + var paragraphId = container.data('paragraph'); + + setCommentBubblePosition( event.pageX, event.pageY ); + + //Prepare to open the div + $("#comment-bubble").data("paragraph", paragraphId); + $("#comment-bubble").addClass('visible'); + + var rootElement = $(this).get(0); + + //Maybe it is needed to handle exceptions here + try { + var selObj = rangy.getSelection(); + var selected_area = rangy.serializeSelection(selObj, true, rootElement); + } catch(e) { + return; + } + form = container.find('.post_comment_box').find('form'); + + //Register the area that has been selected at input.selected_area + if (form.find('input.selected_area').length === 0){ + $('').attr({ + class: 'selected_area', + type: 'hidden', + name: 'comment[comment_paragraph_selected_area]', + value: selected_area + }).appendTo(form) + }else{ + form.find('input.selected_area').val(selected_area) + } + //Register the content being selected at input.comment_paragraph_selected_content + var selected_content = getSelectionText(); + if (form.find('input.selected_content').length === 0){ + $('').attr({ + class: 'selected_content', + type: 'hidden', + name: 'comment[comment_paragraph_selected_content]', + value: selected_content + }).appendTo(form) + }else{ + form.find('input.selected_content').val(selected_content) + } + rootElement.focus(); + cssApplierSelected.toggleSelection(); + clearSelection(); + + //set a one time handler to prevent multiple selections + var fn = function() { + hideAllSelectedAreasExcept(null, '.commented-area-selected'); + $('.comment-paragraph-plugin').off('mousedown', '.comment_paragraph', fn); + } + $('.comment-paragraph-plugin').on('mousedown', '.comment_paragraph', fn); + }); + + function processAnchor(){ + var anchor = window.location.hash; + if(anchor.length==0) return; + var val = anchor.split('-'); //anchor format = #comment-\d+ + if(val.length!=2 || val[0]!='#comment') return; + if($('.comment-paragraph-plugin').length==0) return; + var comment_id = val[1]; + if(!/^\d+$/.test(comment_id)) return; //test for integer + + var url = '/plugin/comment_paragraph/public/comment_paragraph/'+comment_id; + $.ajax(url).done(function(data) { + var button = $('#comment-paragraph-plugin_' + data.paragraph_uuid + ' .side-comments-counter').click(); + $('body').animate({scrollTop: parseInt(button.offset().top)}, 500); + button.click(); + }); + } + + processAnchor(); + + $(document).on('mouseenter', 'li.article-comment', function() { + hideAllSelectedAreasExcept(null, '.commented-area-selected'); + var selected_area = $(this).find('input.paragraph_comment_area').val(); + var container = $(this).closest('.comment-paragraph-plugin'); + var rootElement = container.find('.comment_paragraph')[0]; + + if(selected_area != ""){ + rangy.deserializeSelection(selected_area, rootElement); + cssApplier.toggleSelection(); + } + }); + + $(document).on('mouseleave', 'li.article-comment', function() { + hideAllSelectedAreasExcept(); + + var container = $(this).closest('.comment-paragraph-plugin'); + var selected_area = container.find('input.selected_area').val(); + var rootElement = container.find('.comment_paragraph')[0]; + if(selected_area != ""){ + rangy.deserializeSelection(selected_area, rootElement); + cssApplierSelected.toggleSelection(); + } + clearSelection(); + }); +}); diff --git a/plugins/comment_paragraph/public/images/internet-group-chat.png b/plugins/comment_paragraph/public/images/internet-group-chat.png new file mode 100644 index 0000000..6ffdd65 Binary files /dev/null and b/plugins/comment_paragraph/public/images/internet-group-chat.png differ diff --git a/plugins/comment_paragraph/public/rangy-core.js b/plugins/comment_paragraph/public/rangy-core.js new file mode 100644 index 0000000..8549d10 --- /dev/null +++ b/plugins/comment_paragraph/public/rangy-core.js @@ -0,0 +1,94 @@ +/* + Rangy, a cross-browser JavaScript range and selection library + http://code.google.com/p/rangy/ + + Copyright 2012, Tim Down + Licensed under the MIT license. + Version: 1.2.3 + Build date: 26 February 2012 +*/ +window.rangy=function(){function l(p,u){var w=typeof p[u];return w=="function"||!!(w=="object"&&p[u])||w=="unknown"}function K(p,u){return!!(typeof p[u]=="object"&&p[u])}function H(p,u){return typeof p[u]!="undefined"}function I(p){return function(u,w){for(var B=w.length;B--;)if(!p(u,w[B]))return false;return true}}function z(p){return p&&A(p,x)&&v(p,t)}function C(p){window.alert("Rangy not supported in your browser. Reason: "+p);c.initialized=true;c.supported=false}function N(){if(!c.initialized){var p, +u=false,w=false;if(l(document,"createRange")){p=document.createRange();if(A(p,n)&&v(p,i))u=true;p.detach()}if((p=K(document,"body")?document.body:document.getElementsByTagName("body")[0])&&l(p,"createTextRange")){p=p.createTextRange();if(z(p))w=true}!u&&!w&&C("Neither Range nor TextRange are implemented");c.initialized=true;c.features={implementsDomRange:u,implementsTextRange:w};u=k.concat(f);w=0;for(p=u.length;w["+c.childNodes.length+"]":c.nodeName}function n(c){this._next=this.root=c}function t(c,f){this.node=c;this.offset=f}function x(c){this.code=this[c]; +this.codeName=c;this.message="DOMException: "+this.codeName}var A=l.util;A.areHostMethods(document,["createDocumentFragment","createElement","createTextNode"])||K.fail("document missing a Node creation method");A.isHostMethod(document,"getElementsByTagName")||K.fail("document missing getElementsByTagName method");var q=document.createElement("div");A.areHostMethods(q,["insertBefore","appendChild","cloneNode"])||K.fail("Incomplete Element implementation");A.isHostProperty(q,"innerHTML")||K.fail("Element is missing innerHTML property"); +q=document.createTextNode("test");A.areHostMethods(q,["splitText","deleteData","insertData","appendData","cloneNode"])||K.fail("Incomplete Text Node implementation");var v=function(c,f){for(var k=c.length;k--;)if(c[k]===f)return true;return false};n.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){var c=this._current=this._next,f;if(this._current)if(f=c.firstChild)this._next=f;else{for(f=null;c!==this.root&&!(f=c.nextSibling);)c=c.parentNode;this._next=f}return this._current}, +detach:function(){this._current=this._next=this.root=null}};t.prototype={equals:function(c){return this.node===c.node&this.offset==c.offset},inspect:function(){return"[DomPosition("+i(this.node)+":"+this.offset+")]"}};x.prototype={INDEX_SIZE_ERR:1,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INVALID_STATE_ERR:11};x.prototype.toString=function(){return this.message};l.dom={arrayContains:v,isHtmlNamespace:function(c){var f;return typeof c.namespaceURI== +"undefined"||(f=c.namespaceURI)===null||f=="http://www.w3.org/1999/xhtml"},parentElement:function(c){c=c.parentNode;return c.nodeType==1?c:null},getNodeIndex:H,getNodeLength:function(c){var f;return C(c)?c.length:(f=c.childNodes)?f.length:0},getCommonAncestor:I,isAncestorOf:function(c,f,k){for(f=k?f:f.parentNode;f;)if(f===c)return true;else f=f.parentNode;return false},getClosestAncestorIn:z,isCharacterDataNode:C,insertAfter:N,splitDataNode:function(c,f){var k=c.cloneNode(false);k.deleteData(0,f); +c.deleteData(f,c.length-f);N(k,c);return k},getDocument:O,getWindow:function(c){c=O(c);if(typeof c.defaultView!="undefined")return c.defaultView;else if(typeof c.parentWindow!="undefined")return c.parentWindow;else throw Error("Cannot get a window object for node");},getIframeWindow:function(c){if(typeof c.contentWindow!="undefined")return c.contentWindow;else if(typeof c.contentDocument!="undefined")return c.contentDocument.defaultView;else throw Error("getIframeWindow: No Window object found for iframe element"); +},getIframeDocument:function(c){if(typeof c.contentDocument!="undefined")return c.contentDocument;else if(typeof c.contentWindow!="undefined")return c.contentWindow.document;else throw Error("getIframeWindow: No Document object found for iframe element");},getBody:function(c){return A.isHostObject(c,"body")?c.body:c.getElementsByTagName("body")[0]},getRootContainer:function(c){for(var f;f=c.parentNode;)c=f;return c},comparePoints:function(c,f,k,r){var L;if(c==k)return f===r?0:f=e.childNodes.length?e.appendChild(a):e.insertBefore(a,e.childNodes[j]);return o}function O(a){for(var e,j,o=H(a.range).createDocumentFragment();j=a.next();){e=a.isPartiallySelectedSubtree();j=j.cloneNode(!e);if(e){e=a.getSubtreeIterator();j.appendChild(O(e));e.detach(true)}if(j.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");o.appendChild(j)}return o}function i(a,e,j){var o,E;for(j=j||{stop:false};o=a.next();)if(a.isPartiallySelectedSubtree())if(e(o)=== +false){j.stop=true;return}else{o=a.getSubtreeIterator();i(o,e,j);o.detach(true);if(j.stop)return}else for(o=g.createIterator(o);E=o.next();)if(e(E)===false){j.stop=true;return}}function n(a){for(var e;a.next();)if(a.isPartiallySelectedSubtree()){e=a.getSubtreeIterator();n(e);e.detach(true)}else a.remove()}function t(a){for(var e,j=H(a.range).createDocumentFragment(),o;e=a.next();){if(a.isPartiallySelectedSubtree()){e=e.cloneNode(false);o=a.getSubtreeIterator();e.appendChild(t(o));o.detach(true)}else a.remove(); +if(e.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");j.appendChild(e)}return j}function x(a,e,j){var o=!!(e&&e.length),E,T=!!j;if(o)E=RegExp("^("+e.join("|")+")$");var m=[];i(new q(a,false),function(s){if((!o||E.test(s.nodeType))&&(!T||j(s)))m.push(s)});return m}function A(a){return"["+(typeof a.getName=="undefined"?"Range":a.getName())+"("+g.inspectNode(a.startContainer)+":"+a.startOffset+", "+g.inspectNode(a.endContainer)+":"+a.endOffset+")]"}function q(a,e){this.range=a;this.clonePartiallySelectedTextNodes= +e;if(!a.collapsed){this.sc=a.startContainer;this.so=a.startOffset;this.ec=a.endContainer;this.eo=a.endOffset;var j=a.commonAncestorContainer;if(this.sc===this.ec&&g.isCharacterDataNode(this.sc)){this.isSingleCharacterDataNode=true;this._first=this._last=this._next=this.sc}else{this._first=this._next=this.sc===j&&!g.isCharacterDataNode(this.sc)?this.sc.childNodes[this.so]:g.getClosestAncestorIn(this.sc,j,true);this._last=this.ec===j&&!g.isCharacterDataNode(this.ec)?this.ec.childNodes[this.eo-1]:g.getClosestAncestorIn(this.ec, +j,true)}}}function v(a){this.code=this[a];this.codeName=a;this.message="RangeException: "+this.codeName}function c(a,e,j){this.nodes=x(a,e,j);this._next=this.nodes[0];this._position=0}function f(a){return function(e,j){for(var o,E=j?e:e.parentNode;E;){o=E.nodeType;if(g.arrayContains(a,o))return E;E=E.parentNode}return null}}function k(a,e){if(G(a,e))throw new v("INVALID_NODE_TYPE_ERR");}function r(a){if(!a.startContainer)throw new S("INVALID_STATE_ERR");}function L(a,e){if(!g.arrayContains(e,a.nodeType))throw new v("INVALID_NODE_TYPE_ERR"); +}function p(a,e){if(e<0||e>(g.isCharacterDataNode(a)?a.length:a.childNodes.length))throw new S("INDEX_SIZE_ERR");}function u(a,e){if(h(a,true)!==h(e,true))throw new S("WRONG_DOCUMENT_ERR");}function w(a){if(D(a,true))throw new S("NO_MODIFICATION_ALLOWED_ERR");}function B(a,e){if(!a)throw new S(e);}function V(a){return!!a.startContainer&&!!a.endContainer&&!(!g.arrayContains(ba,a.startContainer.nodeType)&&!h(a.startContainer,true))&&!(!g.arrayContains(ba,a.endContainer.nodeType)&&!h(a.endContainer, +true))&&a.startOffset<=(g.isCharacterDataNode(a.startContainer)?a.startContainer.length:a.startContainer.childNodes.length)&&a.endOffset<=(g.isCharacterDataNode(a.endContainer)?a.endContainer.length:a.endContainer.childNodes.length)}function J(a){r(a);if(!V(a))throw Error("Range error: Range is no longer valid after DOM mutation ("+a.inspect()+")");}function ca(){}function Y(a){a.START_TO_START=ia;a.START_TO_END=la;a.END_TO_END=ra;a.END_TO_START=ma;a.NODE_BEFORE=na;a.NODE_AFTER=oa;a.NODE_BEFORE_AND_AFTER= +pa;a.NODE_INSIDE=ja}function W(a){Y(a);Y(a.prototype)}function da(a,e){return function(){J(this);var j=this.startContainer,o=this.startOffset,E=this.commonAncestorContainer,T=new q(this,true);if(j!==E){j=g.getClosestAncestorIn(j,E,true);o=C(j);j=o.node;o=o.offset}i(T,w);T.reset();E=a(T);T.detach();e(this,j,o,j,o);return E}}function fa(a,e,j){function o(m,s){return function(y){r(this);L(y,$);L(d(y),ba);y=(m?z:C)(y);(s?E:T)(this,y.node,y.offset)}}function E(m,s,y){var F=m.endContainer,Q=m.endOffset; +if(s!==m.startContainer||y!==m.startOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==1){F=s;Q=y}e(m,s,y,F,Q)}}function T(m,s,y){var F=m.startContainer,Q=m.startOffset;if(s!==m.endContainer||y!==m.endOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==-1){F=s;Q=y}e(m,F,Q,s,y)}}a.prototype=new ca;l.util.extend(a.prototype,{setStart:function(m,s){r(this);k(m,true);p(m,s);E(this,m,s)},setEnd:function(m,s){r(this);k(m,true);p(m,s);T(this,m,s)},setStartBefore:o(true,true),setStartAfter:o(false,true),setEndBefore:o(true, +false),setEndAfter:o(false,false),collapse:function(m){J(this);m?e(this,this.startContainer,this.startOffset,this.startContainer,this.startOffset):e(this,this.endContainer,this.endOffset,this.endContainer,this.endOffset)},selectNodeContents:function(m){r(this);k(m,true);e(this,m,0,m,g.getNodeLength(m))},selectNode:function(m){r(this);k(m,false);L(m,$);var s=z(m);m=C(m);e(this,s.node,s.offset,m.node,m.offset)},extractContents:da(t,e),deleteContents:da(n,e),canSurroundContents:function(){J(this);w(this.startContainer); +w(this.endContainer);var m=new q(this,true),s=m._first&&K(m._first,this)||m._last&&K(m._last,this);m.detach();return!s},detach:function(){j(this)},splitBoundaries:function(){J(this);var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=m===y;g.isCharacterDataNode(y)&&F>0&&F0&&s=g.getNodeIndex(m)&&F++;s=0}e(this,m,s,y,F)},normalizeBoundaries:function(){J(this); +var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=function(U){var R=U.nextSibling;if(R&&R.nodeType==U.nodeType){y=U;F=U.length;U.appendData(R.data);R.parentNode.removeChild(R)}},qa=function(U){var R=U.previousSibling;if(R&&R.nodeType==U.nodeType){m=U;var sa=U.length;s=R.length;U.insertData(0,R.data);R.parentNode.removeChild(R);if(m==y){F+=s;y=m}else if(y==U.parentNode){R=g.getNodeIndex(U);if(F==R){y=U;F=sa}else F>R&&F--}}},ga=true;if(g.isCharacterDataNode(y))y.length== +F&&Q(y);else{if(F>0)(ga=y.childNodes[F-1])&&g.isCharacterDataNode(ga)&&Q(ga);ga=!this.collapsed}if(ga)if(g.isCharacterDataNode(m))s==0&&qa(m);else{if(sx";X=P.firstChild.nodeType==3}catch(ta){}l.features.htmlParsingConforms=X;var ka=["startContainer","startOffset","endContainer","endOffset", +"collapsed","commonAncestorContainer"],ia=0,la=1,ra=2,ma=3,na=0,oa=1,pa=2,ja=3;ca.prototype={attachListener:function(a,e){this._listeners[a].push(e)},compareBoundaryPoints:function(a,e){J(this);u(this.startContainer,e.startContainer);var j=a==ma||a==ia?"start":"end",o=a==la||a==ia?"start":"end";return g.comparePoints(this[j+"Container"],this[j+"Offset"],e[o+"Container"],e[o+"Offset"])},insertNode:function(a){J(this);L(a,aa);w(this.startContainer);if(g.isAncestorOf(a,this.startContainer,true))throw new S("HIERARCHY_REQUEST_ERR"); +this.setStartBefore(N(a,this.startContainer,this.startOffset))},cloneContents:function(){J(this);var a,e;if(this.collapsed)return H(this).createDocumentFragment();else{if(this.startContainer===this.endContainer&&g.isCharacterDataNode(this.startContainer)){a=this.startContainer.cloneNode(true);a.data=a.data.slice(this.startOffset,this.endOffset);e=H(this).createDocumentFragment();e.appendChild(a);return e}else{e=new q(this,true);a=O(e);e.detach()}return a}},canSurroundContents:function(){J(this);w(this.startContainer); +w(this.endContainer);var a=new q(this,true),e=a._first&&K(a._first,this)||a._last&&K(a._last,this);a.detach();return!e},surroundContents:function(a){L(a,b);if(!this.canSurroundContents())throw new v("BAD_BOUNDARYPOINTS_ERR");var e=this.extractContents();if(a.hasChildNodes())for(;a.lastChild;)a.removeChild(a.lastChild);N(a,this.startContainer,this.startOffset);a.appendChild(e);this.selectNode(a)},cloneRange:function(){J(this);for(var a=new M(H(this)),e=ka.length,j;e--;){j=ka[e];a[j]=this[j]}return a}, +toString:function(){J(this);var a=this.startContainer;if(a===this.endContainer&&g.isCharacterDataNode(a))return a.nodeType==3||a.nodeType==4?a.data.slice(this.startOffset,this.endOffset):"";else{var e=[];a=new q(this,true);i(a,function(j){if(j.nodeType==3||j.nodeType==4)e.push(j.data)});a.detach();return e.join("")}},compareNode:function(a){J(this);var e=a.parentNode,j=g.getNodeIndex(a);if(!e)throw new S("NOT_FOUND_ERR");a=this.comparePoint(e,j);e=this.comparePoint(e,j+1);return a<0?e>0?pa:na:e>0? +oa:ja},comparePoint:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);if(g.comparePoints(a,e,this.startContainer,this.startOffset)<0)return-1;else if(g.comparePoints(a,e,this.endContainer,this.endOffset)>0)return 1;return 0},createContextualFragment:X?function(a){var e=this.startContainer,j=g.getDocument(e);if(!e)throw new S("INVALID_STATE_ERR");var o=null;if(e.nodeType==1)o=e;else if(g.isCharacterDataNode(e))o=g.parentElement(e);o=o===null||o.nodeName=="HTML"&&g.isHtmlNamespace(g.getDocument(o).documentElement)&& +g.isHtmlNamespace(o)?j.createElement("body"):o.cloneNode(false);o.innerHTML=a;return g.fragmentFromNodeChildren(o)}:function(a){r(this);var e=H(this).createElement("body");e.innerHTML=a;return g.fragmentFromNodeChildren(e)},toHtml:function(){J(this);var a=H(this).createElement("div");a.appendChild(this.cloneContents());return a.innerHTML},intersectsNode:function(a,e){J(this);B(a,"NOT_FOUND_ERR");if(g.getDocument(a)!==H(this))return false;var j=a.parentNode,o=g.getNodeIndex(a);B(j,"NOT_FOUND_ERR"); +var E=g.comparePoints(j,o,this.endContainer,this.endOffset);j=g.comparePoints(j,o+1,this.startContainer,this.startOffset);return e?E<=0&&j>=0:E<0&&j>0},isPointInRange:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);return g.comparePoints(a,e,this.startContainer,this.startOffset)>=0&&g.comparePoints(a,e,this.endContainer,this.endOffset)<=0},intersectsRange:function(a,e){J(this);if(H(a)!=H(this))throw new S("WRONG_DOCUMENT_ERR");var j=g.comparePoints(this.startContainer, +this.startOffset,a.endContainer,a.endOffset),o=g.comparePoints(this.endContainer,this.endOffset,a.startContainer,a.startOffset);return e?j<=0&&o>=0:j<0&&o>0},intersection:function(a){if(this.intersectsRange(a)){var e=g.comparePoints(this.startContainer,this.startOffset,a.startContainer,a.startOffset),j=g.comparePoints(this.endContainer,this.endOffset,a.endContainer,a.endOffset),o=this.cloneRange();e==-1&&o.setStart(a.startContainer,a.startOffset);j==1&&o.setEnd(a.endContainer,a.endOffset);return o}return null}, +union:function(a){if(this.intersectsRange(a,true)){var e=this.cloneRange();g.comparePoints(a.startContainer,a.startOffset,this.startContainer,this.startOffset)==-1&&e.setStart(a.startContainer,a.startOffset);g.comparePoints(a.endContainer,a.endOffset,this.endContainer,this.endOffset)==1&&e.setEnd(a.endContainer,a.endOffset);return e}else throw new v("Ranges do not intersect");},containsNode:function(a,e){return e?this.intersectsNode(a,false):this.compareNode(a)==ja},containsNodeContents:function(a){return this.comparePoint(a, +0)>=0&&this.comparePoint(a,g.getNodeLength(a))<=0},containsRange:function(a){return this.intersection(a).equals(a)},containsNodeText:function(a){var e=this.cloneRange();e.selectNode(a);var j=e.getNodes([3]);if(j.length>0){e.setStart(j[0],0);a=j.pop();e.setEnd(a,a.length);a=this.containsRange(e);e.detach();return a}else return this.containsNodeContents(a)},createNodeIterator:function(a,e){J(this);return new c(this,a,e)},getNodes:function(a,e){J(this);return x(this,a,e)},getDocument:function(){return H(this)}, +collapseBefore:function(a){r(this);this.setEndBefore(a);this.collapse(false)},collapseAfter:function(a){r(this);this.setStartAfter(a);this.collapse(true)},getName:function(){return"DomRange"},equals:function(a){return M.rangesEqual(this,a)},isValid:function(){return V(this)},inspect:function(){return A(this)}};fa(M,ha,function(a){r(a);a.startContainer=a.startOffset=a.endContainer=a.endOffset=null;a.collapsed=a.commonAncestorContainer=null;I(a,"detach",null);a._listeners=null});l.rangePrototype=ca.prototype; +M.rangeProperties=ka;M.RangeIterator=q;M.copyComparisonConstants=W;M.createPrototypeRange=fa;M.inspect=A;M.getRangeDocument=H;M.rangesEqual=function(a,e){return a.startContainer===e.startContainer&&a.startOffset===e.startOffset&&a.endContainer===e.endContainer&&a.endOffset===e.endOffset};l.DomRange=M;l.RangeException=v}); +rangy.createModule("WrappedRange",function(l){function K(i,n,t,x){var A=i.duplicate();A.collapse(t);var q=A.parentElement();z.isAncestorOf(n,q,true)||(q=n);if(!q.canHaveHTML)return new C(q.parentNode,z.getNodeIndex(q));n=z.getDocument(q).createElement("span");var v,c=t?"StartToStart":"StartToEnd";do{q.insertBefore(n,n.previousSibling);A.moveToElementText(n)}while((v=A.compareEndPoints(c,i))>0&&n.previousSibling);c=n.nextSibling;if(v==-1&&c&&z.isCharacterDataNode(c)){A.setEndPoint(t?"EndToStart":"EndToEnd", +i);if(/[\r\n]/.test(c.data)){q=A.duplicate();t=q.text.replace(/\r\n/g,"\r").length;for(t=q.moveStart("character",t);q.compareEndPoints("StartToEnd",q)==-1;){t++;q.moveStart("character",1)}}else t=A.text.length;q=new C(c,t)}else{c=(x||!t)&&n.previousSibling;q=(t=(x||t)&&n.nextSibling)&&z.isCharacterDataNode(t)?new C(t,0):c&&z.isCharacterDataNode(c)?new C(c,c.length):new C(q,z.getNodeIndex(n))}n.parentNode.removeChild(n);return q}function H(i,n){var t,x,A=i.offset,q=z.getDocument(i.node),v=q.body.createTextRange(), +c=z.isCharacterDataNode(i.node);if(c){t=i.node;x=t.parentNode}else{t=i.node.childNodes;t=A12");d.close();var h=c.getIframeWindow(b).getSelection(),D=d.documentElement.lastChild.firstChild;d=d.createRange();d.setStart(D,1);d.collapse(true);h.addRange(d);ha=h.rangeCount==1;h.removeAllRanges();var G=d.cloneRange();d.setStart(D,0);G.setEnd(D,2);h.addRange(d);h.addRange(G);ea=h.rangeCount==2;d.detach();G.detach();Y.removeChild(b)}();l.features.selectionSupportsMultipleRanges=ea; +l.features.collapsedNonEditableSelectionsSupported=ha;var M=false,g;if(Y&&f.isHostMethod(Y,"createControlRange")){g=Y.createControlRange();if(f.areHostProperties(g,["item","add"]))M=true}l.features.implementsControlRange=M;w=W?function(b){return b.anchorNode===b.focusNode&&b.anchorOffset===b.focusOffset}:function(b){return b.rangeCount?b.getRangeAt(b.rangeCount-1).collapsed:false};var Z;if(f.isHostMethod(B,"getRangeAt"))Z=function(b,d){try{return b.getRangeAt(d)}catch(h){return null}};else if(W)Z= +function(b){var d=c.getDocument(b.anchorNode);d=l.createRange(d);d.setStart(b.anchorNode,b.anchorOffset);d.setEnd(b.focusNode,b.focusOffset);if(d.collapsed!==this.isCollapsed){d.setStart(b.focusNode,b.focusOffset);d.setEnd(b.anchorNode,b.anchorOffset)}return d};l.getSelection=function(b){b=b||window;var d=b._rangySelection,h=u(b),D=V?I(b):null;if(d){d.nativeSelection=h;d.docSelection=D;d.refresh(b)}else{d=new x(h,D,b);b._rangySelection=d}return d};l.getIframeSelection=function(b){return l.getSelection(c.getIframeWindow(b))}; +g=x.prototype;if(!J&&W&&f.areHostMethods(B,["removeAllRanges","addRange"])){g.removeAllRanges=function(){this.nativeSelection.removeAllRanges();C(this)};var S=function(b,d){var h=k.getRangeDocument(d);h=l.createRange(h);h.collapseToPoint(d.endContainer,d.endOffset);b.nativeSelection.addRange(N(h));b.nativeSelection.extend(d.startContainer,d.startOffset);b.refresh()};g.addRange=fa?function(b,d){if(M&&V&&this.docSelection.type=="Control")t(this,b);else if(d&&da)S(this,b);else{var h;if(ea)h=this.rangeCount; +else{this.removeAllRanges();h=0}this.nativeSelection.addRange(N(b));this.rangeCount=this.nativeSelection.rangeCount;if(this.rangeCount==h+1){if(l.config.checkSelectionRanges)if((h=Z(this.nativeSelection,this.rangeCount-1))&&!k.rangesEqual(h,b))b=new r(h);this._ranges[this.rangeCount-1]=b;z(this,b,aa(this.nativeSelection));this.isCollapsed=w(this)}else this.refresh()}}:function(b,d){if(d&&da)S(this,b);else{this.nativeSelection.addRange(N(b));this.refresh()}};g.setRanges=function(b){if(M&&b.length> +1)A(this,b);else{this.removeAllRanges();for(var d=0,h=b.length;d1)A(this,b);else d&&this.addRange(b[0])}}else{K.fail("No means of selecting a Range or TextRange was found");return false}g.getRangeAt=function(b){if(b<0||b>=this.rangeCount)throw new L("INDEX_SIZE_ERR");else return this._ranges[b]}; +var $;if(J)$=function(b){var d;if(l.isSelectionValid(b.win))d=b.docSelection.createRange();else{d=c.getBody(b.win.document).createTextRange();d.collapse(true)}if(b.docSelection.type=="Control")n(b);else d&&typeof d.text!="undefined"?i(b,d):C(b)};else if(f.isHostMethod(B,"getRangeAt")&&typeof B.rangeCount=="number")$=function(b){if(M&&V&&b.docSelection.type=="Control")n(b);else{b._ranges.length=b.rangeCount=b.nativeSelection.rangeCount;if(b.rangeCount){for(var d=0,h=b.rangeCount;d0&&j/g,">");break;case 8:h="