diff --git a/Changelog.md b/Changelog.md index ea5b08276f3db5be012e8baf438846ce67635260..deb8081e8c900ab5049cd80ad381841a46095ef4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,10 +9,14 @@ # 0.6.2.0 ## Refactor +* Use string-direction gem for rtl detection [#7181](https://github.com/diaspora/diaspora/pull/7181) +* Reduce i18n.load side effects [#7184](https://github.com/diaspora/diaspora/pull/7184) +* Force jasmine fails on syntax errors [#7185](https://github.com/diaspora/diaspora/pull/7185) ## Bug fixes * Fix fetching comments after fetching likes [#7167](https://github.com/diaspora/diaspora/pull/7167) * Hide 'reshare' button on already reshared posts [#7169](https://github.com/diaspora/diaspora/pull/7169) +* Only reload profile header when changing aspect memberships [#7183](https://github.com/diaspora/diaspora/pull/7183) ## Features * Show spinner when loading comments in the stream [#7170](https://github.com/diaspora/diaspora/pull/7170) diff --git a/Gemfile b/Gemfile index e4614cfd7db769d707666cf9129b0f8ead261ebf..9bf0486c694a757faa5c529daf9ae37d10361d40 100644 --- a/Gemfile +++ b/Gemfile @@ -135,6 +135,10 @@ gem "twitter-text", "1.14.0" gem "ruby-oembed", "0.10.1" gem "open_graph_reader", "0.6.1" +# RTL support + +gem "string-direction", "1.2.0" + # Security Headers gem "secure_headers", "3.5.0" diff --git a/Gemfile.lock b/Gemfile.lock index 73f20f63eeb5df72a748a9091659e09dd872591f..73559ce038019860f42fce5b50c49711904ccb8c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -819,6 +819,8 @@ GEM activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) state_machine (1.2.0) + string-direction (1.2.0) + yard (~> 0.8) swd (1.0.1) activesupport (>= 3) attr_required (>= 0.0.5) @@ -1031,6 +1033,7 @@ DEPENDENCIES spring (= 2.0.0) spring-commands-cucumber (= 1.0.1) spring-commands-rspec (= 1.0.4) + string-direction (= 1.2.0) test_after_commit (= 1.1.0) timecop (= 0.8.1) turbo_dev_assets (= 0.0.2) @@ -1046,4 +1049,4 @@ DEPENDENCIES will_paginate (= 3.1.5) BUNDLED WITH - 1.13.5 + 1.13.6 diff --git a/app/assets/javascripts/app/pages/profile.js b/app/assets/javascripts/app/pages/profile.js index b987ea3876e6fb2bf8d4a5a6d24f3d7efa272073..a2b8143993001a37a995e935d4410e42eab59e63 100644 --- a/app/assets/javascripts/app/pages/profile.js +++ b/app/assets/javascripts/app/pages/profile.js @@ -31,7 +31,6 @@ app.pages.Profile = app.views.Base.extend({ this.streamCollection = _.has(opts, "streamCollection") ? opts.streamCollection : null; this.streamViewClass = _.has(opts, "streamView") ? opts.streamView : null; - this.model.on("change", this.render, this); this.model.on("sync", this._done, this); // bind to global events diff --git a/app/assets/javascripts/app/views/profile_header_view.js b/app/assets/javascripts/app/views/profile_header_view.js index 0fc671b8eecec2c6bf5213999c39c43086066b4c..1522b71e4b2cda6f250fc34e262d1981cf50bf12 100644 --- a/app/assets/javascripts/app/views/profile_header_view.js +++ b/app/assets/javascripts/app/views/profile_header_view.js @@ -15,6 +15,7 @@ app.views.ProfileHeader = app.views.Base.extend({ initialize: function(opts) { this.photos = _.has(opts, 'photos') ? opts.photos : null; this.contacts = _.has(opts, 'contacts') ? opts.contacts : null; + this.model.on("change", this.render, this); $("#mentionModal").on("modal:loaded", this.mentionModalLoaded.bind(this)); $("#mentionModal").on("hidden.bs.modal", this.mentionModalHidden); }, diff --git a/app/assets/javascripts/helpers/i18n.js b/app/assets/javascripts/helpers/i18n.js index 42bd7a8864a747700a1409da679786f0b815b746..e6db0f2e1826e53629bf29248fd68107b79b8c39 100644 --- a/app/assets/javascripts/helpers/i18n.js +++ b/app/assets/javascripts/helpers/i18n.js @@ -18,7 +18,7 @@ Diaspora.I18n = { }, updateLocale: function(locale, data) { - locale.data = $.extend(locale.data, data); + locale.data = $.extend({}, locale.data, data); var rule = locale.data.pluralization_rule; if (typeof rule !== "undefined") { diff --git a/lib/direction_detector.rb b/lib/direction_detector.rb index 9724c80c8da927e473f747d505d02f23dd8f2cb3..c7ffd055adc5b9510cf69349bdf76cb5177a6416 100644 --- a/lib/direction_detector.rb +++ b/lib/direction_detector.rb @@ -2,36 +2,16 @@ # Copyright (c) 2010-2011, 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 - RTL_RANGES = [ - [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 - ] RTL_CLEANER_REGEXES = [ /@[^ ]+|#[^ ]+/u, # mention, tag /^RT[: ]{1}| RT | RT: |[♺♻:]/u # retweet ] def is_rtl? return false if self.strip.empty? - count = 0 - self.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) # otherwise let the first word decide + detector = StringDirection::Detector.new(:dominant) + detector.rtl? self end # Diaspora specific @@ -42,14 +22,4 @@ class String end string.is_rtl? end - - def starts_with_rtl_char?(string = self) - stripped = string.strip - return false if stripped.empty? - char = stripped.unpack('U*').first - RTL_RANGES.each do |limit| - return true if char >= limit[0] && char <= limit[1] - end - return false - end end diff --git a/spec/javascripts/app/views/help_view_spec.js b/spec/javascripts/app/views/help_view_spec.js index 098e7dfcc8ac8f2d9bb35e55dc909728ee59275c..38717a62035130bdb60834f774b09a739f099366 100644 --- a/spec/javascripts/app/views/help_view_spec.js +++ b/spec/javascripts/app/views/help_view_spec.js @@ -2,11 +2,17 @@ describe("app.views.Help", function(){ beforeEach(function(){ gon.appConfig = {chat: {enabled: false}}; this.locale = JSON.parse(spec.readFixture("locale_en_help_json")); + Diaspora.I18n.reset(); Diaspora.I18n.load(this.locale, "en"); this.view = new app.views.Help(); Diaspora.Page = "HelpFaq"; }); + afterEach(function() { + Diaspora.I18n.reset(); + Diaspora.I18n.load(spec.defaultLocale); + }); + describe("render", function(){ beforeEach(function(){ this.view.render(); diff --git a/spec/javascripts/app/views/profile_header_view_spec.js b/spec/javascripts/app/views/profile_header_view_spec.js index 17bfecc81546374168662ac6d0517f9b7806d953..ebd4ecde11f2fc89ba3fc17e058f0bb371fa3223 100644 --- a/spec/javascripts/app/views/profile_header_view_spec.js +++ b/spec/javascripts/app/views/profile_header_view_spec.js @@ -11,6 +11,34 @@ describe("app.views.ProfileHeader", function() { loginAs(factory.userAttrs()); }); + describe("initialize", function() { + it("calls #render when the model changes", function() { + spyOn(app.views.ProfileHeader.prototype, "render"); + this.view.initialize(); + expect(app.views.ProfileHeader.prototype.render).not.toHaveBeenCalled(); + this.view.model.trigger("change"); + expect(app.views.ProfileHeader.prototype.render).toHaveBeenCalled(); + }); + + it("calls #mentionModalLoaded on modal:loaded", function() { + spec.content().append("<div id='mentionModal'></div>"); + spyOn(app.views.ProfileHeader.prototype, "mentionModalLoaded"); + this.view.initialize(); + expect(app.views.ProfileHeader.prototype.mentionModalLoaded).not.toHaveBeenCalled(); + $("#mentionModal").trigger("modal:loaded"); + expect(app.views.ProfileHeader.prototype.mentionModalLoaded).toHaveBeenCalled(); + }); + + it("calls #mentionModalHidden on hidden.bs.modal", function() { + spec.content().append("<div id='mentionModal'></div>"); + spyOn(app.views.ProfileHeader.prototype, "mentionModalHidden"); + this.view.initialize(); + expect(app.views.ProfileHeader.prototype.mentionModalHidden).not.toHaveBeenCalled(); + $("#mentionModal").trigger("hidden.bs.modal"); + expect(app.views.ProfileHeader.prototype.mentionModalHidden).toHaveBeenCalled(); + }); + }); + context("#presenter", function() { it("contains necessary elements", function() { expect(this.view.presenter()).toEqual(jasmine.objectContaining({ diff --git a/spec/javascripts/helpers/i18n_spec.js b/spec/javascripts/helpers/i18n_spec.js index 79dfabe27f1546b6c537fbc494403cb71ed52ab6..0190a454f3ac5cf720dbc0941540f3bc1f6062e6 100644 --- a/spec/javascripts/helpers/i18n_spec.js +++ b/spec/javascripts/helpers/i18n_spec.js @@ -45,6 +45,28 @@ describe("Diaspora.I18n", function() { expect(Diaspora.I18n.locale.data).toEqual(extended); }); + + it("overrides existing translations", function() { + var oldLocale = {name: "Bob"}; + var newLocale = {name: "Alice"}; + Diaspora.I18n.load(oldLocale, "en"); + expect(Diaspora.I18n.locale.data.name).toBe("Bob"); + Diaspora.I18n.load(newLocale, "en"); + expect(Diaspora.I18n.locale.data.name).toBe("Alice"); + + Diaspora.I18n.reset(oldLocale); + expect(Diaspora.I18n.locale.data.name).toBe("Bob"); + Diaspora.I18n.load(newLocale, "en"); + expect(Diaspora.I18n.locale.data.name).toBe("Alice"); + }); + + it("doesn't change locale objects given in ealier calls", function() { + var oldLocale = {name: "Bob"}; + var newLocale = {name: "Alice"}; + Diaspora.I18n.reset(oldLocale); + Diaspora.I18n.load(newLocale, "en"); + expect(oldLocale.name).toBe("Bob"); + }); }); describe("::t", function() { diff --git a/spec/javascripts/onerror-fail.js b/spec/javascripts/onerror-fail.js new file mode 100644 index 0000000000000000000000000000000000000000..21f69b745e325c6e8ab8486b187924e827f7c62e --- /dev/null +++ b/spec/javascripts/onerror-fail.js @@ -0,0 +1,7 @@ +window.onerror = function(errorMsg, url, lineNumber) { + describe("Test suite", function() { + it("shouldn't skip tests because of syntax errors", function() { + fail(errorMsg + " in file " + url + " in line " + lineNumber); + }); + }); +}; diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index 87e516cca0572c94b3247c8c69281d9ee5353421..cec4333070de79c6d45abf8bdbf2f9bff7564768 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -53,6 +53,7 @@ helpers: # - **/*[sS]pec.js # spec_files: + - onerror-fail.js - "**/**/*[sS]pec.js" # src_dir diff --git a/spec/lib/direction_detector_spec.rb b/spec/lib/direction_detector_spec.rb index b19e854db128fff1ffb3b5d79f8ba3ef3a6962ff..9b28700e3387083b82b5110e8032755a224fba6f 100644 --- a/spec/lib/direction_detector_spec.rb +++ b/spec/lib/direction_detector_spec.rb @@ -24,29 +24,6 @@ describe String do let(:hebrew_arabic) { "#{hebrew} #{arabic}" } - describe "#stats_with_rtl_char?" do - it 'returns true or false correctly' do - expect(english.starts_with_rtl_char?).to be false - expect(chinese.starts_with_rtl_char?).to be false - expect(arabic.starts_with_rtl_char?).to be true - expect(hebrew.starts_with_rtl_char?).to be true - expect(hebrew_arabic.starts_with_rtl_char?).to be true - end - - it 'only looks at the first char' do - expect(english_chinese.starts_with_rtl_char?).to be false - expect(chinese_english.starts_with_rtl_char?).to be false - expect(english_arabic.starts_with_rtl_char?).to be false - expect(hebrew_english.starts_with_rtl_char?).to be true - expect(arabic_chinese.starts_with_rtl_char?).to be true - end - - it 'ignores whitespaces' do - expect(" \n \r \t".starts_with_rtl_char?).to be false - expect(" #{arabic} ".starts_with_rtl_char?).to be true - end - end - describe "#is_rtl?" do it 'returns true or false correctly' do expect(english.is_rtl?).to be false @@ -65,17 +42,16 @@ describe String do expect("#{english} #{arabic} #{arabic}".is_rtl?).to be true end - it "fallbacks to the first word if there's no majority" do - expect(hebrew_english.is_rtl?).to be true - expect(english_hebrew.is_rtl?).to be false - expect(arabic_english.is_rtl?).to be true - expect(english_arabic.is_rtl?).to be false - end - it 'ignores whitespaces' do expect(" \n \r \t".is_rtl?).to be false expect(" #{arabic} ".is_rtl?).to be true end + + it "ignores byte order marks" do + expect("\u{feff}".is_rtl?).to be false + expect("\u{feff}#{arabic}".is_rtl?).to be true + expect("\u{feff}#{english}".is_rtl?).to be false + end end describe '#cleaned_is_rtl?' do