From a7a8da2f75bc23e1d75484792ad5d512e9cc447d Mon Sep 17 00:00:00 2001 From: MrZYX Date: Sat, 2 Apr 2011 22:44:27 +0200 Subject: [PATCH] added detection of rtl languages on every user based input --- app/helpers/application_helper.rb | 4 ++ app/views/comments/_comment.html.haml | 2 +- app/views/comments/_comment.mobile.haml | 3 +- app/views/conversations/_conversation.haml | 3 +- app/views/conversations/_show.haml | 2 +- app/views/messages/_message.haml | 2 +- app/views/people/_profile_sidebar.html.haml | 6 +- .../status_messages/_status_message.haml | 2 +- config/assets.yml | 1 + config/initializers/direction_detector.rb | 55 ++++++++++++++++ public/javascripts/application.js | 2 +- public/javascripts/inbox.js | 8 ++- public/javascripts/stream.js | 1 + public/javascripts/view.js | 3 + public/javascripts/web-socket-receiver.js | 2 + .../javascripts/widgets/directionDetector.js | 64 +++++++++++++++++++ public/stylesheets/sass/application.sass | 4 ++ 17 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 config/initializers/direction_detector.rb create mode 100644 public/javascripts/widgets/directionDetector.js diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 944831697..bec1ffc8e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -280,4 +280,8 @@ module ApplicationHelper defaults end + + def rtl?(string) + return (string.cleaned_is_rtl?) ? 'rtl' : '' + end end diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml index c9af008d7..39926fae8 100644 --- a/app/views/comments/_comment.html.haml +++ b/app/views/comments/_comment.html.haml @@ -13,5 +13,5 @@ %time.timeago{:datetime => comment.created_at} = comment.created_at ? timeago(comment.created_at) : timeago(Time.now) - %p + %p{ :class => rtl?(comment.text) } = markdownify(comment.text, :youtube_maps => comment.youtube_titles) diff --git a/app/views/comments/_comment.mobile.haml b/app/views/comments/_comment.mobile.haml index c81e0ff93..30eb78ae4 100644 --- a/app/views/comments/_comment.mobile.haml +++ b/app/views/comments/_comment.mobile.haml @@ -14,5 +14,6 @@ .content .from = person_link(comment.author) - = markdownify(comment.text, :youtube_maps => comment[:youtube_titles]) + %p{ :class => rtl?(comment.text) } + = markdownify(comment.text, :youtube_maps => comment[:youtube_titles]) diff --git a/app/views/conversations/_conversation.haml b/app/views/conversations/_conversation.haml index 2083a53b7..f04729a40 100644 --- a/app/views/conversations/_conversation.haml +++ b/app/views/conversations/_conversation.haml @@ -10,7 +10,8 @@ .message_count = conversation.messages.size - = conversation.subject[0..30] + %div{ :class => rtl?(conversation.subject) } + = conversation.subject[0..30] .last_author .timestamp diff --git a/app/views/conversations/_show.haml b/app/views/conversations/_show.haml index 76c7ccadd..ddefccd07 100644 --- a/app/views/conversations/_show.haml +++ b/app/views/conversations/_show.haml @@ -6,7 +6,7 @@ .span-16.last .conversation_participants .span-9 - %h3 + %h3{ :class => rtl?(conversation.subject) } = conversation.subject .conversation_controls diff --git a/app/views/messages/_message.haml b/app/views/messages/_message.haml index 8617eba9f..ea8f8422e 100644 --- a/app/views/messages/_message.haml +++ b/app/views/messages/_message.haml @@ -11,6 +11,6 @@ %time.timeago{:datetime => message.created_at} = how_long_ago(message) - %p + %p{ :class => rtl?(message.text) } = markdownify(message.text) diff --git a/app/views/people/_profile_sidebar.html.haml b/app/views/people/_profile_sidebar.html.haml index af8fc81dc..0b314caf2 100644 --- a/app/views/people/_profile_sidebar.html.haml +++ b/app/views/people/_profile_sidebar.html.haml @@ -41,12 +41,14 @@ %li %h4 =t('.bio') - = markdownify(person.profile.bio, :newlines => true) + %div{ :class => rtl?(person.profile.bio) } + = markdownify(person.profile.bio, :newlines => true) - unless person.profile.location.blank? %li %h4 =t('.location') - = markdownify(person.profile.location, :newlines => true) + %div{ :class => rtl?(person.profile.location) } + = markdownify(person.profile.location, :newlines => true) %li.span-8.last .span-4 diff --git a/app/views/status_messages/_status_message.haml b/app/views/status_messages/_status_message.haml index 054136ff5..fbb336f44 100644 --- a/app/views/status_messages/_status_message.haml +++ b/app/views/status_messages/_status_message.haml @@ -14,5 +14,5 @@ - for photo in photos[1..photos.size] = link_to (image_tag photo.url(:thumb_small), :class => 'thumb_small'), photo_path(photo) -%p +%p{ :class => rtl?(post.text) } = markdownify(post.text, :youtube_maps => post[:youtube_titles]) diff --git a/config/assets.yml b/config/assets.yml index 2efd4455a..790aff7e1 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -29,6 +29,7 @@ javascripts: - public/javascripts/widgets/alert.js - public/javascripts/widgets/embedder.js - public/javascripts/widgets/timeago.js + - public/javascripts/widgets/directionDetector.js - public/javascripts/view.js - public/javascripts/stream.js - public/javascripts/application.js diff --git a/config/initializers/direction_detector.rb b/config/initializers/direction_detector.rb new file mode 100644 index 000000000..9e71648b9 --- /dev/null +++ b/config/initializers/direction_detector.rb @@ -0,0 +1,55 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. +# Deeply inspired by https://gitorious.org/statusnet/mainline/blobs/master/plugins/DirectionDetector/DirectionDetectorPlugin.php + +class String + + def is_rtl? + count = 0 + self.strip.split(" ").each do |word| + if starts_with_rtl_char?(word) + count += 1 + else + count -= 1 + end + end + return true if count > 0 # more than half of the words are rtl words + return starts_with_rtl_char?(self.strip) # otherwise let the first word decide + end + + # Diaspora specific + def cleaned_is_rtl? + string = String.new(self) + [ /@[^ ]+|#[^ ]+/u, # mention, tag + /^RT[: ]{1}| RT | RT: |[♺♻:]/u, # retweet + /[ \r\t\n]+/ # whitespaces + ].each do |cleaner| + string.gsub!(cleaner, '') + end + string.is_rtl? + end + + private + + def starts_with_rtl_char?(string) + string = self.strip + return false if string.empty? + char = string.unpack('U*').first + limits = [ + [1536, 1791], # arabic, persian, urdu, kurdish, ... + [65136, 65279], # arabic peresent 2 + [64336, 65023], # arabic peresent 1 + [1424, 1535], # hebrew + [64256, 64335], # hebrew peresent + [1792, 1871], # syriac + [1920, 1983], # thaana + [1984, 2047], # nko + [11568, 11647] # tifinagh + ] + limits.each do |limit| + return true if char >= limit[0] && char <= limit[1] + end + return false + end +end diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 97175dd0b..f7f613220 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -25,10 +25,10 @@ $(document).ready(function() { .css("display", "block"); } Diaspora.widgets.timeago.updateTimeAgo(); + Diaspora.widgets.directionDetector.updateBinds(); }); - $("a.paginate").live("click", function() { $(this).css("display", "none"); diff --git a/public/javascripts/inbox.js b/public/javascripts/inbox.js index 00435e90d..6686b9247 100644 --- a/public/javascripts/inbox.js +++ b/public/javascripts/inbox.js @@ -6,13 +6,17 @@ $(document).ready(function(){ $('a.conversation').live('click', function(){ - $.getScript(this.href); + $.getScript(this.href, function() { + Diaspora.widgets.directionDetector.updateBinds(); + }); history.pushState(null, "", this.href); return false; }); $(window).bind("popstate", function(){ - $.getScript(location.href); + $.getScript(location.href, function() { + Diaspora.widgets.directionDetector.updateBinds(); + }); return false; }); diff --git a/public/javascripts/stream.js b/public/javascripts/stream.js index efe7f2cf7..8138e0749 100644 --- a/public/javascripts/stream.js +++ b/public/javascripts/stream.js @@ -11,6 +11,7 @@ var Stream = { $(".status_message_delete").tipsy({trigger: 'hover', gravity: 'n'}); Diaspora.widgets.timeago.updateTimeAgo(); + Diaspora.widgets.directionDetector.updateBinds(); $stream.not(".show").delegate("a.show_post_comments", "click", Stream.toggleComments); //audio linx Stream.setUpAudioLinks(); diff --git a/public/javascripts/view.js b/public/javascripts/view.js index d9c9cf932..6a2afb251 100644 --- a/public/javascripts/view.js +++ b/public/javascripts/view.js @@ -58,6 +58,9 @@ var View = { $.facebox.settings.closeImage = '/images/facebox/closelabel.png' $.facebox.settings.loadingImage = '/images/facebox/loading.gif' $('a[rel*=facebox]').facebox(); + $(document).bind('reveal.facebox', function() { + Diaspora.widgets.directionDetector.updateBinds(); + }); /* facebox 'done' buttons */ $("a[rel*=close]").live('click', function(){ $.facebox.close() }); diff --git a/public/javascripts/web-socket-receiver.js b/public/javascripts/web-socket-receiver.js index c541c6729..5b39cc415 100644 --- a/public/javascripts/web-socket-receiver.js +++ b/public/javascripts/web-socket-receiver.js @@ -126,6 +126,7 @@ var WebSocketReceiver = { } Diaspora.widgets.timeago.updateTimeAgo(); + Diaspora.widgets.directionDetector.updateBinds(); }, processLike: function(postId, html) { @@ -157,6 +158,7 @@ var WebSocketReceiver = { showMessage(); } Diaspora.widgets.timeago.updateTimeAgo(); + Diaspora.widgets.directionDetector.updateBinds(); } }, diff --git a/public/javascripts/widgets/directionDetector.js b/public/javascripts/widgets/directionDetector.js new file mode 100644 index 000000000..54b5f502c --- /dev/null +++ b/public/javascripts/widgets/directionDetector.js @@ -0,0 +1,64 @@ +/* Copyright (c) 2010, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ +/* Modified version of https://gitorious.org/statusnet/mainline/blobs/master/plugins/DirectionDetector/jquery.DirectionDetector.js */ + +Diaspora.widgets.add("directionDetector", function() { + + this.start = function() { + Diaspora.widgets.directionDetector.updateBinds(); + }; + + this.isRTL = function(str) { + if(typeof str != typeof "" || str.length<1) + return false; + var cc = str.charCodeAt(0); + if(cc>=1536 && cc<=1791) // arabic, persian, ... + return true; + if(cc>=65136 && cc<=65279) // arabic peresent 2 + return true; + if(cc>=64336 && cc<=65023) // arabic peresent 1 + return true; + if(cc>=1424 && cc<=1535) // hebrew + return true; + if(cc>=64256 && cc<=64335) // hebrew peresent + return true; + if(cc>=1792 && cc<=1871) // Syriac + return true; + if(cc>=1920 && cc<=1983) // Thaana + return true; + if(cc>=1984 && cc<=2047) // NKo + return true; + if(cc>=11568 && cc<=11647) // Tifinagh + return true; + return false; + }; + + this.cleaner = new RegExp('@[^ ]+|^RT[: ]{1}| RT | RT: |[♺♻:]+', 'g'); + + this.binds = []; + + this.updateBinds = function() { + $.each(Diaspora.widgets.directionDetector.binds, function(i, v) {v.unbind('keyup', Diaspora.widgets.directionDetector.updateDirection);}); + Diaspora.widgets.directionDetector.binds = []; + + $("textarea").each(Diaspora.widgets.directionDetector.bind); + $("input[type='text']").each(Diaspora.widgets.directionDetector.bind); + $("input[type='search']").each(Diaspora.widgets.directionDetector.bind); + }; + + this.bind = function() { + $(this).one('keyup', Diaspora.widgets.directionDetector.updateDirection); + Diaspora.widgets.directionDetector.binds.push($(this)); + }; + + this.updateDirection = function() { + tArea = $(this); + var cleaned = tArea.val().replace(Diaspora.widgets.directionDetector.cleaner, '').replace(/^[ ]+/, ''); + if(Diaspora.widgets.directionDetector.isRTL(cleaned)) + tArea.css('direction', 'rtl'); + else + tArea.css('direction', 'ltr'); + }; +}); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index ad1677acf..3b5ab5dab 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -47,6 +47,10 @@ form :font :size auto +.rtl + :direction rtl + :text-align right + .hidden :display none